home *** CD-ROM | disk | FTP | other *** search
/ ShareWare OnLine 2 / ShareWare OnLine Volume 2 (CMS Software)(1993).iso / prog / pbwiz17.zip / PBWIZ.DOC < prev    next >
Text File  |  1993-06-21  |  93KB  |  2,407 lines

  1.                 The PowerBASIC Wizard's Library
  2.                 =-----------------------------=
  3.                           Version 1.7
  4.  
  5.      PBWIZ  Copyright (c) 1991-1993  Thomas G. Hanlin III
  6.  
  7.  
  8.  
  9. This is the PowerBasic Wizard's Library, a collection of BASIC
  10. and assembly language routines for use with PowerBasic 3.0 by
  11. Spectra Publishing. This collection is protected by copyright,
  12. but may be distributed according to the following conditions:
  13.  
  14.    All PBWiz files must be distributed together in unmodified
  15.    form. No files may be removed or added.
  16.  
  17. YOU USE THIS LIBRARY AT YOUR OWN RISK. It has been tested by me
  18. on my own computer, but I will not assume any responsibility for
  19. any problems which PBWiz may cause you. If you encounter a
  20. problem, please let me know about it, and I will do my best to
  21. verify and repair the error.
  22.  
  23. Shareware is not free software. It operates on a "try before you
  24. buy" basis. It is expected that if you find PBWiz useful, you
  25. will register your copy. You may not use PBWiz routines in
  26. programs intended for distribution unless you have registered.
  27.  
  28. Registration entitles you to receive the latest version of
  29. PBWiz, complete with full source code in assembly language and
  30. BASIC. The assembly code is designed for the MASM 6.0 assembler
  31. and may require modifications for use with other assemblers. See
  32. the ORDER.FRM file for ordering information.
  33.  
  34.                        Table of Contents                  page 2
  35.  
  36.  
  37.  
  38.  Synopsis and Legal Info .................................... 1
  39.  
  40.  Table of Contents .......................................... 2
  41.  
  42.  Overview ................................................... 3
  43.  
  44.  Library Utilities .......................................... 4
  45.  
  46.  ANSI emulation ............................................. 5
  47.  
  48.  Archives ................................................... 6
  49.  
  50.  Dates and Times ............................................ 8
  51.  
  52.  Disk Directories ........................................... 9
  53.  
  54.  Equipment Info ............................................ 11
  55.  
  56.  Extended Math ............................................. 15
  57.  
  58.  Graphics .................................................. 19
  59.  
  60.  Joystick .................................................. 25
  61.  
  62.  Keyboard .................................................. 26
  63.  
  64.  Memory (EMS) .............................................. 30
  65.  
  66.  Memory (XMS) .............................................. 33
  67.  
  68.  Mouse Support ............................................. 35
  69.  
  70.  SoundBlaster .............................................. 38
  71.  
  72.  Strings ................................................... 41
  73.  
  74.  Telecommunications ........................................ 44
  75.  
  76.  Text-mode Video ........................................... 48
  77.  
  78.  Credits ................................................... 52
  79.  
  80.                            Overview                       page 3
  81.  
  82.  
  83.  
  84. Any program that uses any of the PBWiz routines must DECLARE
  85. them appropriately. To make this easy, I've created a single
  86. file which contains all of the necessary declarations. Put the
  87. following line at the top of your program to use it:
  88.  
  89.    $INCLUDE "pbwiz.inc"
  90.  
  91. The DECLARE statements contained in the PBWIZ.INC file tell
  92. PowerBASIC how to behave when it runs into any PBWiz routines.
  93. Unfortunately, PowerBASIC assumes you actually need all of the
  94. DECLAREd routines in your program-- so, use this only for
  95. development, until you're sure which PBWiz routines are needed
  96. by the program. At that point, you can remove the $INCLUDE, and
  97. put in only the DECLAREs for the routines you actually use in
  98. your program. That will make your .EXE much smaller.
  99.  
  100. The DECLAREs just tell PowerBASIC what routines to expect. You
  101. must also tell PowerBASIC where to find the routines:
  102.  
  103.    $LINK "pbwiz.pbl"
  104.  
  105. It is no longer necessary to $LINK each unit separately, since
  106. PowerBasic 3.0 provides true libraries. A library is nothing
  107. more or less than a collection of units. Instead of having to
  108. remember which routine is in which unit, you just put the unit
  109. in a library, and let the linker figure it out. Look, Ma-- no
  110. hands! The ability to use libraries adds terrific power to the
  111. BASIC language. But of course, you expect power of PowerBASIC!
  112.  
  113. Note that $LINK is only used in your main program. If you write
  114. units which use PBWiz, you must include the appropriate DECLAREs
  115. in each unit, but not $LINK. The $LINK metacommand goes in the
  116. main program which makes use of the unit, not in the unit
  117. itself.
  118.  
  119.                       Library Utilities                   page 4
  120.  
  121.  
  122.  
  123. The PBLIB.EXE librarian that comes with PowerBASIC 3.0 provides
  124. most of the features you'd expect of a library utility. Its
  125. interactive design is convenient for light library handling. If
  126. you're using a serious library like PBWiz, however, you may find
  127. it inadequate. PBWiz comes with a set of utilities which extend
  128. PBLIB to be more useful with large libraries. These utilities
  129. act as a shell for PBLIB, feeding it appropriate values based on
  130. your input-- so they are perfectly compatible.
  131.  
  132. If you wish to update a library with new versions of routines
  133. that are already in the library, use LIBUPD:
  134.  
  135.    LIBUPD libname module
  136.  
  137. Here, "libname" is the name of the library. The .PBL extension
  138. is optional. The "module" is the name of the object file (.OBJ
  139. or .PBU) to update. You may list multiple modules, separated by
  140. spaces, and the module name(s) may contain wildcards. LIBUPD
  141. will delete existing modules by that name (regardless of
  142. extension) from the library before adding the current version of
  143. the module to the library. If you don't specify a module
  144. extension, LIBUPD will use .OBJ if available, or .PBU otherwise.
  145.  
  146. The LIBUPD utility can be used to add new modules to a library.
  147. However, if you are building a new library or are adding modules
  148. that you know are not already in an existing library, it is more
  149. efficient to use LIBADD, which works the same way:
  150.  
  151.    LIBADD libname module
  152.  
  153. You can also list the contents of a library in any of a number
  154. of ways, using LIBLIST:
  155.  
  156.    LIBLIST libname switch
  157.  
  158. The switch may come before or after the libname. Only one switch
  159. may be used at a time. The switch may be:
  160.  
  161.    /EXTERNALS      list externals (routines the module needs)
  162.    /MODULES        list modules
  163.    /PUBLICS        list publics (routines the module provides)
  164.    /RAW            list the info that PBLIB gives, unchanged
  165.  
  166. The switch may be abbreviated to as little as one character. If
  167. you choose /EXTERNALS or /PUBLICs, the result will be an
  168. alphabetized list, cross-referenced with the name of the module
  169. that contains the symbols. If you choose /MODULES, an
  170. alphabetized list of the names of the modules contained in the
  171. library will be provided. If you choose /RAW, you'll get the
  172. same results as you would by going directly through PBLIB.EXE.
  173. The name of the output file will be libname.LST.
  174.  
  175. These utilities create temporary files before the final output.
  176. You will get faster results if you set an environment variable
  177. TEMP to point to a RAMdisk.
  178.  
  179.                         ANSI Emulation                    page 5
  180.  
  181.  
  182.  
  183. The ANSI emulator allows you to display text with ANSI codes
  184. without any need for ANSI.SYS or other ANSI drivers.  All you
  185. have to do is replace any PRINT statements with a call to the
  186. ANSI emulator:
  187.  
  188.    PRINT St$;
  189.  
  190. turns into
  191.  
  192.    AnsiPrint St$
  193.  
  194. Note that this does not include a carriage return and linefeed.
  195. If you want one, you'll have to add it explicitly:
  196.  
  197.    St$ = St$ + CHR$(13) + CHR$(10)
  198.    AnsiPrint St$
  199.  
  200. Other ASCII codes are supported as well, including CHR$(7)
  201. [bell], CHR$(8) [backspace], and CHR$(12) [formfeed].
  202.  
  203. For a list of ANSI display codes, see your DOS manual or check
  204. with your local BBS.
  205.  
  206.                            Archives                       page 6
  207.  
  208.  
  209.  
  210. When I started in the microcomputer industry, there was a small
  211. variety of file archivers, all (more or less) compatible. They
  212. did not provide compression, which was relegated to another
  213. large selection of more-or-less compatible utilities. Then came
  214. SEA's ARC. It was very slow, but it did compression as well as
  215. archiving, and included CRC checks so you could know whether the
  216. files were intact. It swept the BBS scene in short order,
  217. becoming the new standard. A few other archivers competed on
  218. about a level footing, providing only minor variances on the ARC
  219. theme. Then SEA decided to sue one of their more successful
  220. competitors, Phil Katz (PKARC). The end result was PKZIP, which
  221. totally blew ARC away-- a useful hint to companies that choose
  222. to litigate instead of innovate. In the chaos resulting from the
  223. breaking of the former ARC standard, many other archivers came
  224. into being: ARJ, LZH, PAK, ZOO, et al.
  225.  
  226. PBWiz helps resolve the confusion by providing a single set of
  227. routines which allow you to view the contents of archives in any
  228. of the above-mentioned formats: ARJ, LZH, PAK, ZIP, ZOO, and
  229. even the antique ARC protocol. It also handles self-extracting
  230. EXE files of the forms produced by ARJ, LHARC and ZIP2EXE. Only
  231. archive directories are provided at this time. Other formats
  232. will also be added as they arise. If you have details on the
  233. format of an archive that you'd like me to add to PBWiz, please
  234. send them my way.
  235.  
  236. Viewing archive directories is handled in roughly the same
  237. fashion as you might view a DOS file directory. This makes it
  238. possible to treat an archive and a subdirectory in a similar
  239. manner, as long as the archive doesn't contain archives itself.
  240.  
  241. Please note that the archive routines use OPEN FOR BINARY.
  242. Unfortunately, PowerBasic ignores OPTION BASE BINARY in
  243. subprograms, so it is necessary for you to include the following
  244. statement in your main program if you use the archive routines:
  245.  
  246.    OPTION BASE BINARY 1
  247.  
  248. This causes binary files to be numbered starting at 1, as is
  249. traditional for BASIC, rather than the peculiar 0 default that
  250. PowerBasic uses only for binary files. I'll add a set of file
  251. routines to PBWiz in the future, at which point this silliness
  252. can be dispensed with.
  253.  
  254. When you're looking for the first file in an archive, use the
  255. FindFirstA function. You must specify the archive name and a
  256. file name. The archive name may include a drive and path
  257. specification, and does not need to have the archive extension.
  258. If you leave off the extension, FindFirstA will use the first
  259. archive it comes across that matches the rest of the
  260. specification. Note that the archive specification may not
  261. contain wildcards. In contrast, the search file name may not
  262. contain drive or path specs, but may contain wildcards.
  263.  
  264.    FindFirstA Archive$, Filename$, ErrCode%
  265.  
  266.                            Archives                       page 7
  267.  
  268.  
  269.  
  270. If there are no files to be found, or if the archive
  271. specification was bad, an error code will be returned. If there
  272. was no error, there may well be more files to be found. You can
  273. find each of them with FindNextA:
  274.  
  275.    FindNextA ErrCode%
  276.  
  277. Of course, just finding a matching file doesn't do you much good
  278. unless you can retrieve information about it. You can use any of
  279. the following routines to provide information about a matched
  280. file:
  281.  
  282.    Nam$ = GetNameA$
  283.    Dat$ = GetDateA$
  284.    Tim$ = GetTimeA$
  285.    CRC$ = GetCRCA$
  286.    StorageMethod$ = GetStoreA$
  287.  
  288.    GetSizeA OriginalSize&, CurrentSize&
  289.  
  290. When you're done viewing an archive, be sure to close it:
  291.  
  292.    CloseA
  293.  
  294. Let's try an example, to view all files in an archive:
  295.  
  296.    $INCLUDE "pbwiz.inc"
  297.    $LINK "pbwiz.pbl"
  298.    ' set Archive$ to the name of an archive here!
  299.    FindFirstA Archive$, "*.*", ErrCode%
  300.    DO UNTIL ErrCode%
  301.       PRINT GetNameA$
  302.       FindNextA ErrCode%
  303.    LOOP
  304.    FCloseA
  305.  
  306. This program fragment assumes that you have set Archive$ to the
  307. name of an archive. It might be convenient to set it to the
  308. command line for testing purposes:
  309.  
  310.    Archive$ = UCASE$(LTRIM$(RTRIM$(COMMAND$)))
  311.  
  312.                         Dates and Times                   page 8
  313.  
  314.  
  315.  
  316. This unit allows you to validate and compare dates. It also
  317. provides the day of the week, given the date. Dates may not be
  318. before the year 1900. Date strings may be in the form "01/01/91"
  319. or "01-01-1991" (the delimiter is not significant and years may
  320. be two or four digits; two-digit years will be assumed to be in
  321. the 20th century).
  322.  
  323. Let's start off with date validation. It's often important to
  324. know if a date entered into your program is a valid date.
  325.  
  326.    IF GoodDate%(DateSt$) THEN PRINT "The date is valid."
  327.  
  328. It can also be helpful to know on which day of the week a given
  329. date falls.
  330.  
  331.    Day$ = WeekDay$(DateSt$)
  332.  
  333. There are many useful things you can accomplish by turning a
  334. date into a number which represents that date (or vice versa).
  335. This allows you to compare two dates, which is important if you
  336. want to sort by date; find out what the date will be in a given
  337. number of days, or what it was some number of days ago; find the
  338. number of days between two dates; display a calendar; and so
  339. forth. This is easy to do with PBWiz:
  340.  
  341.    DateNr& = Date2Num&(DateSt$)
  342.  
  343.    DateSt$ = Num2Date$(DateNr&)
  344.  
  345. The DateNr& represents the number of days since January 1, 1900.
  346. This is less than 65,535 for dates that go up to around the year
  347. 2070 or so, so you may wish to store the dates in compressed
  348. two-byte form if your required range of dates is not that large:
  349.  
  350.    CrunchDate% = CVI(LEFT$(MKL$(DateNr&), 2))
  351.  
  352. This can be reversed simply:
  353.  
  354.    DateNr& = CVL(MKI$(CrunchDate%) + STRING$(2, 0))
  355.  
  356. Note that dates crunched this way are only useful for storage
  357. purposes, since the numbers greater than 32,767 are stored as
  358. negative numbers due to the signed integer format BASIC uses.
  359. You must uncompress them before doing any comparisons or date
  360. calculations. Still, for a savings of 50%, it may be worth the
  361. hassle to convert back and forth.
  362.  
  363.                        Disk Directories                   page 9
  364.  
  365.  
  366.  
  367. This unit lets you read disk directories and retrieve the same
  368. information the DIR command shows, plus the file attribute. A
  369. string buffer is used to allow recursive directory searching.
  370.  
  371. When you're looking for the first file in a directory, use the
  372. FindFirstF function. You must provide a search filespec, which
  373. may contain a drive and path specification and use wildcards.
  374. You must also provide a search attribute, which may be any
  375. combination of the following added together:
  376.  
  377.    Normal          0      (nothing special)
  378.    Read Only       1      file can be read, but not written to
  379.    Hidden          2      file is "invisible"
  380.    System          4      special DOS system file
  381.    Subdirectory   16      subdirectory
  382.    Archive        32      (used by some backup utilities)
  383.  
  384. Note that you will always get all files that match any of your
  385. search specs. For example, if your search attribute was 18,
  386. you'd get normal files, hidden files, normal subdirectories and
  387. hidden subdirectories. If you want to be more specific, you will
  388. have to test the file attribute of the resulting file.
  389.  
  390. You must provide a string buffer for the directory search
  391. routine. This buffer must be 64 characters long and should be
  392. initialized before each call to FindFirstF. By using different
  393. buffer strings, you can search more than one directory at a
  394. time, or perform recursive searches through directories.
  395.  
  396.    Buffer$ = SPACE$(64)
  397.    FindFirstF Buffer$, Filename$, ErrCode%
  398.  
  399. If there are no files to be found, or if the file specification
  400. was bad, an error code will be returned. If there was no error,
  401. there may well be more files to be found. You can find each of
  402. them with FindNextF:
  403.  
  404.    FindNextF Buffer$, ErrCode%
  405.  
  406. Of course, just finding a matching file doesn't do you much good
  407. unless you can retrieve information about it. You can use any of
  408. the following routines to provide information about a matched
  409. file:
  410.  
  411.    FilAttr% = GetAttrF%(Buffer$)       ' file attribute
  412.    FilName$ = GetNameF$(Buffer$)       ' file name
  413.    FilDate$ = GetDateF$(Buffer$)       ' file date
  414.    FilSize& = GetSizeF&(Buffer$)       ' file size
  415.    FilTime$ = GetTimeF$(Buffer$)       ' file time
  416.  
  417.                        Disk Directories                  page 10
  418.  
  419.  
  420.  
  421. The file attribute can be most readily decoded with a series of
  422. ANDs. For example, to test for a subdirectory, you'd use:
  423.  
  424.    IF FilAttr% AND 16 THEN PRINT "subdirectory"
  425.  
  426. Let's put all these routines together and see what it takes to
  427. make a quick'n'dirty DIR-style utility.
  428.  
  429.    $INCLUDE "pbwiz.inc"
  430.    $LINK "pbwiz.pbl"
  431.    SearchName$ = COMMAND$  ' get search spec from command line
  432.    Buffer$ = SPACE$(64)    ' set up buffer
  433.    FindFirstF Buffer$, SearchName$, SearchAttr%, ErrCode%
  434.    DO UNTIL ErrCode%
  435.       PRINT GetNameF$(Buffer$), GetSizeF&(Buffer$),
  436.       PRINT GetDateF$(Buffer$), GetTimeF$(Buffer$),
  437.       FilAttr% = GetAttrF%(Buffer$)
  438.       IF FilAttr% AND 1 THEN PRINT "Read Only  ";
  439.       IF FilAttr% AND 2 THEN PRINT "Hidden  ";
  440.       IF FilAttr% AND 4 THEN PRINT "System  ";
  441.       IF FilAttr% AND 16 THEN PRINT "Subdirectory  ";
  442.       IF FilAttr% AND 32 THEN PRINT "Backup  ";
  443.       PRINT
  444.       FindNextA ErrCode%
  445.    LOOP
  446.  
  447. Doesn't take much, does it? Now you can add disk directory
  448. handling to your program with a bare minimum of effort!
  449.  
  450.                         Equipment Info                   page 11
  451.  
  452.  
  453.  
  454. The equipment unit gives you information about the computing
  455. environment. This includes both installed software and hardware.
  456.  
  457. The first function allows you to determine if an "enhanced"
  458. keyboard (101-key) is installed. It may not be able to figure
  459. out what the keyboard is on some older not-quite-clone PCs, in
  460. which case it will take the safe way out and report that there
  461. is no enhanced keyboard. This function returns -1 if there is an
  462. enhanced keyboard present, 0 if not.
  463.  
  464.    Enhanced% = KbdType%
  465.  
  466. Want to know the type of processor (CPU) being used? Can do!
  467.  
  468.    CPU% = Processor%
  469.  
  470. The results will be reported as a number which can be decoded as
  471. follows:
  472.  
  473.    0    NEC V20
  474.    1    8088 or 8086
  475.    2    80186
  476.    3    80286
  477.    4    80386
  478.    5    80486
  479.  
  480. Maybe you'd like to check for a CD-ROM drive:
  481.  
  482.    Drives% = CDROM%
  483.  
  484. This tells you how many logical drives exist, if there is a
  485. CD-ROM available. If not, it will return 0. Note that the CD-ROM
  486. installation check conflicts with the GRAPHICS.COM installation
  487. check for DOS 4.0, due to some screw-up at IBM or Microsoft. I'm
  488. not yet sure whether DOS 5.0 is similarly afflicted.
  489.  
  490. The number of floppy drives installed is retrieved like this:
  491.  
  492.    Drives% = Floppies%
  493.  
  494.                         Equipment Info                   page 12
  495.  
  496.  
  497.  
  498. There may be up to four floppy drives in a system; however, the
  499. AT CMOS data area only directly supports two. This makes it easy
  500. to find out what kind of drives the first two are, but not the
  501. second two. Oh well, guess we'll have to settle for what we can
  502. get, right?
  503.  
  504.    FloppyType Drive1, Drive2
  505.  
  506. The results from FloppyType are returned as follows:
  507.  
  508.    0    no drive
  509.    1    5 1/4"    360K
  510.    2    5 1/4"    1.2M
  511.    3    3 1/2"    720K
  512.    4    3 1/2"    1.44M
  513.  
  514. Result codes of 5-7 are available, but not yet defined. One
  515. might guess that the 2.88M drive supported by DOS 5.0 will be
  516. drive type 5. Has anybody seen one of those puppies yet?
  517.  
  518. New memory types sure have burgeoned over the years... expanded,
  519. extended, and now XMS. There are routines to check all of these:
  520.  
  521.    BaseExt& = AllExtMem&     ' extended memory installed
  522.    NowExt& = GetExtM&        ' BIOS extended memory available
  523.  
  524.    GetEMSm TotalPages%, FreePages%    ' expanded memory
  525.  
  526.    GetXMSm LargestFree&, TotalFree&   ' XMS memory
  527.  
  528. When you're dealing with extended memory, whether it be
  529. BIOS-type or using the XMS standard, the results are returned in
  530. kilobytes. Multiply 'em by 1024 to convert to bytes. When you're
  531. dealing with expanded memory (EMS), the results are in pages of
  532. 16,384 bytes.
  533.  
  534. I might note, by the way, that Microsoft seems to have
  535. intentionally crippled the XMS standard. It can only support a
  536. maximum of 64 megabytes. This may seem like a lot now, but I can
  537. remember when my first PC (a Compaq portable) was the awe of the
  538. neighborhood with 384K RAM! A few years down the road, the
  539. artificial limitations of XMS are going to be a nuisance.
  540.  
  541. A few more routines to get the versions of the EMS and XMS
  542. drivers, if any:
  543.  
  544.    GetEMSv MajorV%, MinorV%
  545.    GetXMSv MajorV%, MinorV%
  546.  
  547. These return the major and minor version numbers as two separate
  548. integers. For example, EMS 4.0 would return major version 4,
  549. minor version 0.
  550.  
  551.                         Equipment Info                   page 13
  552.  
  553.  
  554.  
  555. It's nice to know a little about the operating environment. With
  556. the below routines, you can find out what the DOS version is;
  557. what version of 4DOS, if any, is in use; and whether Microsoft
  558. Windows is running.
  559.  
  560.    GetDOSv MajorV%, MinorV%
  561.    Get4DOSv MajorV%, MinorV%
  562.    WinCheck MajorV%, MinorV%
  563.  
  564. These return results as major and minor version numbers, as
  565. discussed on the previous page. The Get4DOSv and WinCheck
  566. routines return zeroes if 4DOS and Windows, respectively, are
  567. not available.
  568.  
  569. There are a couple of curious features of GetDOSv to keep in
  570. mind. If the version is 10 or higher, you're running in OS/2
  571. compatibility mode. DOS version 10 is actually OS/2 1.0, version
  572. 20 is OS/2 2.0, and so on. Secondly, if you're using DOS 5.0,
  573. the version reported may not be 5.0-- DOS 5.0 can be told to
  574. reply with a lower version number to allow some older software
  575. (which checks for a specific DOS version) to run properly.
  576.  
  577. One final routine that should be of some value is the one that
  578. allows you to find out what kind of display is available. It
  579. tells you the specific adapter and whether the display is color
  580. or monochrome. There is one case in which it can be confused,
  581. however-- if the adapter is CGA, the display is assumed to be
  582. color, since there is no way for the computer to know any
  583. differently. So, although this routine provides a good idea of
  584. what is available, it would be a good idea to provide an option
  585. to tell the program that a monochrome display is attached.
  586. Microsoft normally uses "/B" for this purpose, so that might be
  587. a good standard to stick with.
  588.  
  589.    GetDisplay Adapter%, Mono%
  590.    IF Mono% THEN
  591.       PRINT "Monochrome monitor"
  592.    ELSE
  593.       PRINT "Color monitor"
  594.    END IF
  595.    SELECT CASE Adapter%
  596.       CASE 1: PRINT "MDA"
  597.       CASE 2: PRINT "Hercules"
  598.       CASE 3: PRINT "CGA"
  599.       CASE 4: PRINT "EGA"
  600.       CASE 5: PRINT "MCGA"
  601.       CASE 6: PRINT "VGA"
  602.    END SELECT
  603.  
  604.                         Equipment Info                   page 14
  605.  
  606.  
  607.  
  608. Aside from some of the oldest semi-clones, it's possible to find
  609. out what sort of machine you're using by looking at a specific
  610. data byte. You can access this as follows:
  611.  
  612.    Machine% = PCType%
  613.  
  614. The result will need decoding. Here are some known values:
  615.  
  616.    255   PC or XT
  617.    254   XT
  618.    253   PCjr
  619.    252   PC AT
  620.    251   XT
  621.    250   PS/2 Model 30
  622.    249   PC Convertible
  623.    248   PS/2 Model 70 or 80
  624.    154   Compaq Portable
  625.     45   Compaq Portable
  626.  
  627. Likewise, all but some of the oldest semi-clones maintain a BIOS
  628. date value which tells you how old the BIOS ROM is. This can be
  629. retrieved with the following routine:
  630.  
  631.    BIOSdate$ = PCDate$
  632.  
  633. If your program is running on one of the rare old machines which
  634. don't maintain a valid BIOS date, this routine will return "No
  635. Date " instead of an actual date.
  636.  
  637. As far as a program is concerned, DR DOS is essentially the same
  638. as MS-DOS. Still, it's always nice to know what sort of
  639. operating environment you have. You can find out whether your
  640. program is running under DR DOS with the following function:
  641.  
  642.    IF DRDOS% THEN PRINT "It's DR DOS" ELSE PRINT "MS-DOS"
  643.  
  644. The number of serial and parallel ports available can be readily
  645. obtained:
  646.  
  647.    PRINT "Serial ports: "; CommPorts%
  648.    PRINT "Parallel ports: "; PrtPorts%
  649.  
  650.                          Extended Math                   page 15
  651.  
  652.  
  653.  
  654. The extended math unit provides an expression evaluator and
  655. extensions to BASIC's math. The math extensions include
  656. hyperbolic and inverse trig functions, a few handy constants,
  657. conversions, and more.
  658.  
  659. The expression evaluator allows you to find the result of an
  660. expression contained in a string. Normal algebraic precedence is
  661. used, e.g. 4+3*5 evaluates to 19. The usual numeric operators
  662. (*, /, +, -, ^) are supported (multiply, divide, add, subtract,
  663. and raise to a power). Use of negative numbers is just fine, of
  664. course. Parentheses for overriding the default order of
  665. operations are also supported.
  666.  
  667. You may use either a double asterisk ("**") or a caret ("^")
  668. symbols to indicate exponentiation.
  669.  
  670. The constant PI is recognized, as are the following functions:
  671.    ABS    absolute value        INT    integer
  672.    ACOS   inverse cosine        LOG    natural log
  673.    ASIN   inverse sine          SIN    sine
  674.    ATAN   inverse tangent       SQR    square root
  675.    COS    cosine                TAN    tangent
  676.    FRAC   fraction
  677.  
  678. Trig functions expect angles in radians.
  679.  
  680. To evaluate an expression, you pass it to the evaluator as a
  681. string. You will get back either an error code or a
  682. single-precision result. Try this example to see how the
  683. expression evaluator works:
  684.  
  685.    $STACK 8192
  686.    $INCLUDE "pbwiz.inc"
  687.    $LINK "pbwiz.pbl"
  688.    DO
  689.       INPUT "Expression? "; Expr$
  690.       IF LEN(Expr$) THEN
  691.          Evaluate Expr$, Result!, ErrCode%
  692.          IF ErrCode% THEN
  693.             PRINT "Invalid expression.  Error = "; ErrCode%
  694.          ELSE
  695.             PRINT "Result: "; Result!
  696.          END IF
  697.       END IF
  698.    LOOP WHILE LEN(Expr$)
  699.    END
  700.  
  701. The expression evaluator uses recursion and requires more than
  702. the default amount of stack space, which is why the $STACK
  703. metacommand is used. See CALC.BAS for a working demo.
  704.  
  705.                          Extended Math                   page 16
  706.  
  707.  
  708.  
  709. The new math functions are pretty much self-explanatory, so I'll
  710. just list them here. General notes on assumptions and
  711. constraints are given at the end of this chapter.
  712.  
  713.    Result! = ArcCosHS!(Nr!)       ' inverse hyperbolic cosine
  714.    Result! = ArcSinHS!(Nr!)       ' inverse hyperbolic sine
  715.    Result! = ArcTanHS!(Nr!)       ' inverse hyperbolic tangent
  716.    Result! = ArcCosS!(Nr!)        ' arc cosine (1 >= Nr >= -1)
  717.    Result! = ArcCotS!(Nr!)        ' inverse cotangent
  718.    Result! = ArcCotHS!(Nr!)       ' arc hyperbolic cotangent
  719.    Result! = ArcCscS!(Nr!)        ' inverse cosecant
  720.    Result! = ArcCscHS!(Nr!)       ' inverse hyperbolic cosecant
  721.    Result! = ArcSecS!(Nr!)        ' inverse secant
  722.    Result! = ArcSecHS!(Nr!)       ' inverse hyperbolic secant
  723.    Result! = ArcSinS!(Nr!)        ' arc sine   (1 >= Nr >= -1)
  724.    Result! = CeilS!(Nr!)          ' smallest integer >= Nr
  725.    Result! = Cent2Fahr!(Nr!)      ' centigrade to Fahrenheit
  726.    Result! = CosHS!(Nr!)          ' hyperbolic cosine
  727.    Result! = CotHS!(Nr!)          ' hyperbolic cotangent
  728.    Result! = CotS!(Nr!)           ' cotangent
  729.    Result! = CscHS!(Nr!)          ' hyperbolic cosecant
  730.    Result! = CscS!(Nr!)           ' cosecant
  731.    Result! = Deg2RadS!(Nr!)       ' convert degrees to radians
  732.    e! = eS!                       ' the constant "e"
  733.    Result! = ErfS!(Nr!)           ' error function
  734.    Result! = FactS!(Nr%)          ' factorial
  735.    Result! = Fahr2Cent!(Nr!)      ' Fahrenheit to centigrade
  736.    Result! = FloorS!(Nr!)         ' largest integer <= Nr
  737.    Result! = Kg2Pound!(Nr!)       ' convert kilograms to pounds
  738.    Pi! = PiS!                     ' the constant "pi"
  739.    Result! = Pound2Kg!(Nr!)       ' convert pounds to kilograms
  740.    Result! = Rad2DegS!(Nr!)       ' convert radians to degrees
  741.    Result! = SecHS!(Nr!)          ' hyperbolic secant
  742.    Result! = SecS!(Nr!)           ' secant
  743.    Result! = SinHS!(Nr!)          ' hyperbolic sine
  744.    Result! = TanHS!(Nr!)          ' hyperbolic tangent
  745.  
  746.                          Extended Math                   page 17
  747.  
  748.  
  749.  
  750.    Result# = ArcCosD#(Nr#)        ' arc cosine  (1 >= Nr >= -1)
  751.    Result# = ArcCosHD#(Nr#)       ' inverse hyperbolic cosine
  752.    Result# = ArcCotD#(Nr#)        ' inverse cotangent
  753.    Result# = ArcCotHD#(Nr#)       ' arc hyperbolic cotangent
  754.    Result# = ArcCscD#(Nr#)        ' inverse cosecant
  755.    Result# = ArcCscHD#(Nr#)       ' inverse hyperbolic cosecant
  756.    Result# = ArcSecD#(Nr#)        ' inverse secant
  757.    Result# = ArcSecHD#(Nr#)       ' inverse hyperbolic secant
  758.    Result# = ArcSinD#(Nr#)        ' arc sine    (1 >= Nr >= -1)
  759.    Result# = ArcSinHD#(Nr#)       ' inverse hyperbolic sine
  760.    Result# = ArcTanHD#(Nr#)       ' inverse hyperbolic tangent
  761.    Result# = CeilD#(Nr#)          ' smallest integer >= Nr
  762.    Result# = CosHD#(Nr#)          ' hyperbolic cosine
  763.    Result# = CotD#(Nr#)           ' cotangent
  764.    Result# = CotHD#(Nr#)          ' hyperbolic cotangent
  765.    Result# = CscD#(Nr#)           ' cosecant
  766.    Result# = CscHD#(Nr#)          ' hyperbolic cosecant
  767.    Result# = Deg2RadD#(Nr#)       ' convert degrees to radians
  768.    e# = eD#                       ' the constant "e"
  769.    Result# = ErfD#(Nr#)           ' error function
  770.    Result# = FactD#(Nr%)          ' factorial
  771.    Result# = FloorD#(Nr#)         ' largest integer <= Nr
  772.    Pi# = PiD#                     ' the constant "pi"
  773.    Result# = Rad2DegD#(Nr#)       ' convert radians to degrees
  774.    Result# = SecD#(Nr#)           ' secant
  775.    Result# = SecHD#(Nr#)          ' hyperbolic secant
  776.    Result# = SinHD#(Nr#)          ' hyperbolic sine
  777.    Result# = TanHD#(Nr#)          ' hyperbolic tangent
  778.  
  779.                          Extended Math                   page 18
  780.  
  781.  
  782.  
  783.    Result% = GCDI%(Nr1%, Nr2%)    ' greatest common denominator
  784.    Result% = RotateL%(Nr%, Count%) ' rotate left
  785.    Result% = RotateR%(Nr%, Count%) ' rotate right
  786.    Result% = ShiftL%(Nr%, Count%) ' shift left
  787.    Result% = ShiftR%(Nr%, Count%) ' shift right
  788.  
  789.    Result& = GCDL&(Nr1&, Nr2&)    ' greatest common denominator
  790.    Result& = RotateLL&(Nr&, Count%) ' rotate left
  791.    Result& = RotateRL&(Nr&, Count%) ' rotate right
  792.    Result& = ShiftLL&(Nr&, Count%) ' shift left
  793.    Result& = ShiftRL&(Nr&, Count%) ' shift right
  794.  
  795. The ceiling and floor functions are generally used in rounding.
  796.  
  797. Like BASIC's trig functions, the PBWiz trig functions expect the
  798. angle to be in radians. Conversion functions are provided in
  799. case you prefer degrees.
  800.  
  801. "Arc" and "inverse" are used interchangeably (e.g., an arc sine
  802. is the same as an inverse sine).
  803.  
  804. Note that there is no ArcTanS! or ArcTanD# function for the
  805. simple reason that BASIC supplies an ATN function.
  806.  
  807. Constants are expressed to the maximum precision available.
  808.  
  809. If you are not familiar with variable postfix symbols, here's a
  810. brief summary:
  811.  
  812.    Symbol   Meaning             Range (approximate)
  813.    ------   --------            -------------------------------
  814.      %      integer             +- 32767
  815.      &      long integer        +- 2 * 10^9
  816.      !      single precision    +- 1 * 10^38
  817.      #      double precision    +- 1 * 10^308
  818.  
  819. See PowerBASIC's online help for further details.
  820.  
  821.                        Graphics Support                  page 19
  822.  
  823.  
  824.  
  825. I was rather surprised to find that PowerBASIC lacks support for
  826. one of the standard VGA modes-- SCREEN 13, the 320x200 256-color
  827. mode. I immediately decided to add support for that mode: dots,
  828. lines, boxes, polygons, and (of course) text. While I was at it,
  829. I thought I'd add support for a nonstandard VGA mode: 360x480 in
  830. 256 colors. This mode will work on almost any plain VGA system,
  831. although it might not work on some older not-quite-compatible
  832. setups. SuperVGA modes are also supported for adapters based on
  833. the popular Tseng 4000 chip set and for any VESA-compatible
  834. SVGA.
  835.  
  836.    13    320x200, 256 colors, 40x25 text, any VGA
  837.    N0    360x480, 256 colors, 45x60 text, almost any VGA
  838.    N5     ? x ? , 256 colors, ? x ? text, Tseng 4000 SVGA
  839.    GV     ? x ?,  ??? colors, ? x ? text, VESA compatibles
  840.  
  841. The graphics routines all use the same nomenclature: a G,
  842. followed by a mode number, followed by the specific name. This
  843. generic naming convention is used so that this chapter can refer
  844. to all available modes. For example, if I say "G#Color" and
  845. you're using mode 13, you'd actually use "G13Color" in your
  846. program. Ok?
  847.  
  848. There are some differences in how the various sets of graphics
  849. routines need to be initialized and shut down. I'll give you an
  850. outline of the differences here and follow it up with more
  851. detailed information in the case of the SVGA modes. The VESA
  852. SVGA mode, GV, should be handled like this:
  853.  
  854.    GGVInit              ' initialize VESA handler
  855.    GGVMode ModeNr%      ' enter graphics mode
  856.    ' *** your main program goes here ***
  857.    GGVDone              ' exit graphics mode
  858.  
  859. The Tseng 4000 SVGA mode, N5, also needs initialization. Its
  860. handling is closer to that of the other modes, however:
  861.  
  862.    GN5Init ModeNr%, PixelsWide%, PixelsHigh%
  863.    GN5Mode 1            ' enter graphics mode
  864.    ' *** your main program goes here ***
  865.    GN5Mode 0            ' exit graphics mode
  866.  
  867. For the other modes, no special initialization is required. You
  868. just use G#Mode with a non-zero value to enter graphics mode, or
  869. zero to restore text mode. The text mode is assumed to be the
  870. normal 80x25 color mode. If this would not be your choice, you
  871. can safely use the PowerBasic SCREEN statement to pick a mode
  872. that's more to your liking.
  873.  
  874.                        Graphics Support                  page 20
  875.  
  876.  
  877.  
  878. Here's an example for typical handling of one of the non-SVGA
  879. modes (in this case, mode 13):
  880.  
  881.    G13Mode 1            ' enter graphics mode
  882.    ' *** your main program goes here ***
  883.    G13Mode 0            ' exit graphics mode
  884.  
  885. The non-SVGA modes have their actual mode number built into the
  886. routines themselves. In the case of SVGA modes, the routines
  887. were designed for more flexibility, since SVGAs come in a wide
  888. variety of configurations.
  889.  
  890. In the case of the Tseng 4000 modes, N5, you specify the BIOS
  891. mode number, along with the screen resolution, when setting up
  892. the routines. Only 256-color modes are supported. The BIOS mode,
  893. width, height, and amount of video memory required to support
  894. the mode may be any of the following:
  895.  
  896.       &H2D     640 x 350    256k
  897.       &H2E     640 x 480    512k
  898.       &H2F     640 x 400    256k
  899.       &H30     800 x 600    512k
  900.       &H38    1024 x 768     1M
  901.  
  902. In the case of the VESA modes, GV, you specify the VESA mode
  903. number when setting up the routines. To provide the greatest
  904. amount of flexibility, VESA modes are not predefined. Rather
  905. than picking a specific mode number, you ask VESA which modes
  906. are available on the current computer, and pick the mode that
  907. best suits your needs from the choices offered.
  908.  
  909. First, it's wise to make sure VESA support is available:
  910.  
  911.    VesaVersion MajorV%, MinorV%
  912.  
  913. This routine returns the version number of the VESA driver,
  914. split into major and minor parts. If VESA isn't available, both
  915. numbers will be zero.
  916.  
  917. Determining which VESA modes are available works something like
  918. scanning a disk or archive directory. You start by requesting
  919. the first mode. Any of a variety of functions can be used to
  920. find out the mode resolution and colors, whether it's a text or
  921. monochrome mode, and so forth. If the mode is unsuitable or
  922. you'd like to check further, you request the next mode number.
  923. It will return a mode of -1 if there are no more modes.
  924.  
  925. A working demonstration of these routines is presented on the
  926. next page, and may also be found in the file VESAINFO.BAS.
  927.  
  928.                        Graphics Support                  page 21
  929.  
  930.  
  931.  
  932.    $INCLUDE "pbwiz.inc"
  933.    $LINK "pbwiz.pbl"
  934.    DEFINT A-Z
  935.  
  936.    VesaVersion MajorV, MinorV
  937.    IF MajorV = 0 AND MinorV = 0 THEN
  938.       PRINT "Sorry, but you do not have VESA support."
  939.       END
  940.    END IF
  941.  
  942.    VMode = VesaFindFirst
  943.    DO UNTIL VMode = -1
  944.       CLS
  945.       PRINT "VESA Mode : &H"; HEX$(VMode)
  946.       PRINT "Mode Type : ";
  947.       IF VesaIsText THEN PRINT "Text "; ELSE PRINT "Graphics ";
  948.       IF VesaIsMono THEN PRINT "(mono)" ELSE PRINT "(color)"
  949.       PRINT "Resolution:"; VesaScrWidth; "x"; VesaScrHeight;
  950.       IF VesaIsText THEN PRINT "chars" ELSE PRINT "pixels"
  951.       PRINT "Char Size :"; VesaChrWidth; "x"; VesaChrHeight
  952.       PRINT "Colors    :"; VesaColors&
  953.       PRINT
  954.       PRINT "-- press a key to view next available mode --"
  955.       VMode = VesaFindNext
  956.       DO
  957.       LOOP WHILE LEN(INKEY$)
  958.       DO
  959.          ky$ = INKEY$
  960.       LOOP UNTIL LEN(ky$)
  961.       IF ky$ = CHR$(27) OR ky$ = CHR$(3) THEN EXIT DO
  962.    LOOP
  963.    END
  964.  
  965.  
  966.  
  967. The VESA mode information functions can be summarized, so:
  968.  
  969.    VesaFindFirst%       get first available mode number
  970.    VesaFindNext%        get next available mode number
  971.  
  972.    VesaIsMono%          whether mode is mono (-1 yes, 0 no)
  973.    VesaIsText%          whether mode is text (-1 yes, 0 no)
  974.  
  975.    VesaScrHeight%       screen height (characters or pixels)
  976.    VesaScrWidth%        screen width (characters or pixels)
  977.  
  978.    VesaChrHeight%       character font height (pixels)
  979.    VesaChrWidth%        character font width (pixels)
  980.  
  981.    VesaColors&          colors (color numbers) in mode
  982.  
  983.                        Graphics Support                  page 22
  984.  
  985.  
  986.  
  987. One difference between BASIC and PBWiz is that, instead of each
  988. "draw" command requiring a color parameter as in BASIC, the
  989. PBWiz library provides a separate color command:
  990.  
  991.    G#Color Foreground%, Background%
  992.  
  993. The "foreground" color is used by all graphics routines. The
  994. background color is used by the G#Cls routine. Both foreground
  995. and background colors are used in by G#Write and G#WriteLn.
  996.  
  997. Here is a list of the corresponding routines, first BASIC, then
  998. PBWiz (replace the "#" with the appropriate mode number):
  999.  
  1000.    ' get the color of a specified point
  1001.    colour% = POINT(x%, y%)
  1002.    colour% = G#GetPel%(x%, y%)
  1003.  
  1004.    ' set the color of a specified point
  1005.    PSET (x%, y%), colour%
  1006.    G#Color colour%, backgnd%
  1007.    G#Plot x%, y%
  1008.  
  1009.    ' draw a line of a specified color
  1010.    LINE (x1%, y1%) - (x2%, y2%), colour%
  1011.    G#Color colour%, backgnd%
  1012.    G#Line x1%, y1%, x2%, y2%
  1013.  
  1014.    ' draw a box frame of a specified color
  1015.    LINE (x1%, y1%) - (x2%, y2%), colour%, B
  1016.    G#Color colour%, backgnd%
  1017.    G#Box x1%, y1%, x2%, y2%, 0
  1018.  
  1019.    ' draw a box of a specified color and fill it in
  1020.    LINE (x1%, y1%) - (x2%, y2%), colour%, BF
  1021.    G#Color colour%, backgnd%
  1022.    G#Box x1%, y1%, x2%, y2%, 1
  1023.  
  1024.    ' clear the screen and home the cursor
  1025.    CLS
  1026.    G#Cls
  1027.  
  1028.    ' get the current cursor position
  1029.    Row% = CSRLIN: Column% = POS(0)
  1030.    G#GetLocate Row%, Column%
  1031.  
  1032.    ' set the current cursor position
  1033.    LOCATE Row%, Column%
  1034.    G#Locate Row%, Column%
  1035.  
  1036.    ' display a string without a carriage return and linefeed
  1037.    PRINT St$;
  1038.    G#Write St$
  1039.  
  1040.    ' display a string with a carriage return and linefeed
  1041.    PRINT St$
  1042.    G#WriteLn St$
  1043.  
  1044.                        Graphics Support                  page 23
  1045.  
  1046.  
  1047.  
  1048. If you need to print a number rather than a string, just use the
  1049. BASIC function STR$ to convert it. If you don't want a leading
  1050. space, use this approach:
  1051.  
  1052.    St$ = LTRIM$(STR$(Number))
  1053.  
  1054. The PBWiz library has other routines which have no BASIC
  1055. equivalent. One allows you to get the current colors:
  1056.  
  1057.    G#GetColor Foreground%, Background%
  1058.  
  1059. Circles and ellipses can be drawn with the Ellipse routine. This
  1060. is similar to the BASIC CIRCLE statement. You specify the center
  1061. of the ellipse (X,Y), plus the X and Y radius values:
  1062.  
  1063.    G#Ellipse CenterX%, CenterY%, XRadius%, YRadius%
  1064.  
  1065. A circle is an ellipse with a constant radius. So, to draw a
  1066. circle, just set both radius values to the same value.
  1067.  
  1068. As well as the usual points, lines, and ellipses, PBWiz also
  1069. allows you to draw regular polygons: triangles, squares,
  1070. pentagons, hexagons, and so on.
  1071.  
  1072.    G#Polygon X%, Y%, Radius%, Vertices%, Angle!
  1073.  
  1074. The X% and Y% values represent the coordinates of the center of
  1075. the polygon. The Radius% is the radius of the polygon (as if you
  1076. were fitting it into a circle). Vertices% is the number of
  1077. angles (also the number of sides) for the polygon to have.
  1078. Angle! specifies the rotation of the polygon, and is specified
  1079. in radians.
  1080.  
  1081. When it comes to 256-color graphics modes, PowerBasic provides
  1082. no way to get or set the palette. The 256-color modes allow you
  1083. to individually select the actual color to assign to a given
  1084. color number. The modes are said to have 256 colors because you
  1085. may use up to 256 at a time (numbered 0 to 255). You may choose
  1086. the color for a given color number from a much larger palette--
  1087. a bit under 262,144 colors, in fact. Color 0 is black by
  1088. default, for example, but there's no reason you can't redefine
  1089. it to mean pale turquoise. In order to do this, you must choose
  1090. the proper color by blending three available colors-- red,
  1091. green, and blue-- in varying amounts. Each may have an intensity
  1092. from 0 (off) to 63 (bright). If you set both RED and BLUE to 32
  1093. and GREEN to 0, for example, you'd get a medium purple color. It
  1094. may take some experimentation to get just the color you want.
  1095. Note that the intensities are finely graded: you are unlikely to
  1096. see any difference between an intensity of 0 and an intensity of
  1097. 3 in a color, for example.
  1098.  
  1099.                        Graphics Support                  page 24
  1100.  
  1101.  
  1102.  
  1103. The basic routines for getting and setting palette colors are as
  1104. follows. Remember that the color number may be 0-255, while each
  1105. of the intensities can be 0-63.
  1106.  
  1107.    GetPalRGB ColorNr%, RedValue%, GreenValue%, BlueValue%
  1108.    SetPalRGB ColorNr%, RedValue%, GreenValue%, BlueValue%
  1109.  
  1110. If you're not concerned with the actual intensity levels, you
  1111. may find it more convenient to treat the color value as a unit,
  1112. stored in a long integer. You can do that directly:
  1113.  
  1114.    ColorValue& = GetPal& (ColorNr%)
  1115.    SetPal ColorNr%, ColorValue%
  1116.  
  1117. It's also easy enough to convert between the formats:
  1118.  
  1119.    ColorValue& = JoinRGB (RedValue%, GreenValue%, BlueValue%)
  1120.    SplitRGB ColorValue&, RedValu%, GreenValu%, BlueValue%
  1121.  
  1122. The most obvious aspect of palettes is in the ability to select
  1123. a set of colors suited to a specific application. When showing a
  1124. picture of the sea, you might want mostly blues and greens, for
  1125. instance. There are other implications in the ability to quickly
  1126. change a color across the entire screen, however. It allows for
  1127. simple animation, the ability to fade in or fade out, and other
  1128. interesting effects. Let your imagination run loose and
  1129. experiment a little! You'll be surprised by the power of palette
  1130. manipulation.
  1131.  
  1132.                            Joystick                      page 25
  1133.  
  1134.  
  1135.  
  1136. There's little enough to say about the joystick. A PC may have
  1137. up to two of them. Normally, a joystick has two buttons, and
  1138. returns a pair of coordinates which describe the position in
  1139. which it is being held. The coordinates vary depending on the
  1140. individual joystick, the computer involved, and other factors,
  1141. so it is wise to provide a calibration routine to customize your
  1142. program for the joystick(s) involved.
  1143.  
  1144. The FlightStick joystick has a dial for throttle control, which
  1145. is available only in a one-joystick setup. The throttle value is
  1146. returned as the Y coordinate of an imaginary second joystick.
  1147.  
  1148. Coordinates (X, Y), where X is the horizontal and Y the vertical
  1149. coordinate, range from around 0 to around 150 on my FlightStick.
  1150. Since these values may vary significantly, however, they are
  1151. returned as words.
  1152.  
  1153. The state of the joystick buttons may be checked individually or
  1154. all at once. If you need to know the state of multiple buttons,
  1155. it is faster to do it all at once, but you may do it either way.
  1156.  
  1157.    A1% = JButtonA1%     ' button 1 on 1st joystick
  1158.    A2% = JButtonA2%     ' button 2 on 1st joystick
  1159.    B1% = JButtonB1%     ' button 1 on 2nd joystick
  1160.    B2% = JButtonB2%     ' button 2 on 2nd joystick
  1161.  
  1162.    JButtons A1%, A2%, B1%, B2%    ' all buttons
  1163.  
  1164. The joystick positions are handled internally by a rather
  1165. unfortunate method involving a timer, due to the design of the
  1166. joystick interface. IBM's early PC strategy was to do everything
  1167. possible in software, which lead to rather inefficient joystick
  1168. and CGA handling. In the case of the joystick, this makes it
  1169. important to get all coordinates at once.
  1170.  
  1171.    JPos AX%, AY%, BX%, BY%        ' all joystick positions
  1172.  
  1173. Joystick support is handled through a set of BIOS routines which
  1174. were added to the PC around 1985. These routines are not present
  1175. in some of the oldest PCs.
  1176.  
  1177.                        Keyboard Control                  page 26
  1178.  
  1179.  
  1180.  
  1181. The keyboard is not a particularly exciting or glamorous device.
  1182. In fact, we tend to forget about it except when it gets in the
  1183. way. Sometimes it's a hardware problem-- squishy or clacking
  1184. keys, or perhaps a commonly-used key placed in an out-of-the-way
  1185. location. Then again, sometimes it's the software that's the
  1186. problem. There are many aspects of keyboard control, not all of
  1187. which are necessarily related to input. This unit will let you
  1188. handle the keyboard in ways you may not have realized were
  1189. possible. Better yet, it can help make keyboard control easier
  1190. than the users of your programs dreamed possible.
  1191.  
  1192. Let's start out with keyboard output. Yep, not input-- output.
  1193. We can stuff up to 15 keys into the keyboard buffer. Why would
  1194. we ever want to do this? Perhaps to allow your program to pop-up
  1195. a TSR automatically, to start another program after your program
  1196. ends, or for creating key macros. You can enter extended key
  1197. codes (such as function keys) by using CHR$(0) before the scan
  1198. code.
  1199.  
  1200.    TypeIn St$
  1201.  
  1202. The usual keyboard action is somewhat sluggish. We can make it a
  1203. lot crisper by changing the key repeat rate and the delay before
  1204. repeating begins. This will work on ATs, but not PC/XT systems.
  1205.  
  1206.    SpeedKey RepDelay%, RepRate%
  1207.  
  1208. The delay may be 0-3 (1 by default):
  1209.  
  1210.    0      250 milliseconds
  1211.    1      500 milliseconds
  1212.    2      750 milliseconds
  1213.    3        1 second
  1214.  
  1215. The repeat rate may be 0-31 (11 by default). The larger the
  1216. number, the slower the speed-- 0 is around 30 cps, and 31 is
  1217. around 2 cps.
  1218.  
  1219. I generally prefer to have the keyboard cranked up to full
  1220. speed, using RepDelay% and RepRate% both set to zero. This may
  1221. be a bit too zippy for some people. Experiment with it to see
  1222. what you like best.
  1223.  
  1224. Of course, there may be reasons to make keyboard repeat less
  1225. sensitive instead! That might be a good idea in programs written
  1226. for small children, for example. You can adjust the keyboard
  1227. equally well in either direction.
  1228.  
  1229.                        Keyboard Control                  page 27
  1230.  
  1231.  
  1232.  
  1233. PowerBASIC allows you to control one of the keys which can
  1234. interrupt your program, namely the Break key. There's another
  1235. dangerous key which PBWiz allows you to control-- the PrtSc
  1236. (PrintScreen) key. PrtSc may not seem like a hazard at first
  1237. glance, but if it's pressed by accident with no printer ready,
  1238. or in a graphics mode which PrtSc doesn't understand how to
  1239. print, the results can be pretty messy. So, we let you turn it
  1240. off or back on:
  1241.  
  1242.    SetPrtSc PrtScON%
  1243.  
  1244. Use 0 to turn it off or anything else to turn it back on. If you
  1245. turn off PrtSc, you MUST remember to turn it back on again
  1246. before your program ends! Otherwise, an interrupt vector will
  1247. point into nowhere, causing probable chaos the next time PrtSc
  1248. is pressed.
  1249.  
  1250. Regardless of whether you've turned the PrtSc key off, you can
  1251. print the screen yourself just as if PrtSc had been pressed:
  1252.  
  1253.    PrintScreen
  1254.  
  1255. Now here's a strange one for you. When IBM brought out the
  1256. 101-key keyboard, called the "enhanced" keyboard, they did
  1257. something bizarre to the BIOS. They still allowed old keyboard
  1258. calls to work, but they filtered out the new key codes so no one
  1259. would see them. This made sure that no one would be able to use
  1260. the capabilities of the "enhanced" keyboard without rewriting
  1261. their programs. So, the keyboard has been around for years, and
  1262. there are still few programs that even notice when you press
  1263. F11. PowerBASIC v2.1 does not support the enhanced keyboard at
  1264. all. Fortunately, PBWiz -does-. You can find out if an enhanced
  1265. keyboard is installed with the KbdType% function, which is in
  1266. the Equipment unit.
  1267.  
  1268. If there is an enhanced keyboard installed, you can activate it
  1269. like so:
  1270.  
  1271.    SetEnhKbd Enhanced%
  1272.  
  1273. With enhanced keyboard support activated, all key requests that
  1274. used the old services are translated to the new services. So,
  1275. SetEnhKbd affects INKEY$ and other BASIC functions as well as
  1276. other PBWiz keyboard routines. Note that you MUST deactivate
  1277. enhanced keyboard support before ending your program. Otherwise,
  1278. an interrupt vector will point into nowhere, probably causing a
  1279. crash on the next keypress!
  1280.  
  1281.                        Keyboard Control                  page 28
  1282.  
  1283.  
  1284.  
  1285. Speaking of INKEY$, I have a neat little function for you. It
  1286. works like INKEY$, but it doesn't remove the key from the
  1287. keyboard buffer:
  1288.  
  1289.    ky$ = ScanKey$
  1290.  
  1291. How is this handy? Well, let's suppose you're writing something
  1292. that will work like the DIR or TYPE commands in DOS. It will
  1293. display what may be a long listing, which you'd like to be able
  1294. to pause with Ctrl-S or cancel with Ctrl-C. Trouble is, if you
  1295. use INKEY$ and the user was using "type ahead" to store another
  1296. command, you've just wiped out his command while looking for
  1297. those control codes. With ScanKey$, you can check the key
  1298. nondestructively.
  1299.  
  1300. On the other hand, if you're about to request important input,
  1301. you may not want to chance having it answered from results of
  1302. the keyboard buffer-- could be that the user meant those keys
  1303. for another purpose. In that case, it's a good approach to clear
  1304. out the keyboard buffer just before the input:
  1305.  
  1306.    ClearKbd
  1307.  
  1308. No keyboard unit would be complete without a selection of
  1309. routines to check the shift states and get or set the keyboard
  1310. toggles. Let's start with the toggles, which are so called
  1311. because they get toggled from one state to another:
  1312.  
  1313.     PRINT "Caps Lock  : ";
  1314.     IF CapsOn% THEN PRINT "ON" ELSE PRINT "OFF"
  1315.     PRINT "Insert     : ";
  1316.     IF InsertOn% THEN PRINT "ON" ELSE PRINT "OFF"
  1317.     PRINT "Num Lock   : ";
  1318.     IF NumOn% THEN PRINT "ON" ELSE PRINT "OFF"
  1319.     PRINT "Scroll Lock: ";
  1320.     IF ScrollOn% THEN PRINT "ON" ELSE PRINT "OFF"
  1321.  
  1322. You can also turn the toggles off or on. It's courteous to
  1323. restore the original toggle states once you end your program, so
  1324. you might want to save the original values for that purpose.
  1325. Then again, I guess that doesn't apply if your program is
  1326. designed for the specific purpose of setting the toggles!
  1327.  
  1328.    SetCaps CapsLock%
  1329.    SetInsert InsertKey%
  1330.    SetNum NumLock%
  1331.    SetScroll ScrollLock%
  1332.  
  1333. Does anyone actually use ScrollLock for anything? Just
  1334. wondering...
  1335.  
  1336.                        Keyboard Control                  page 29
  1337.  
  1338.  
  1339.  
  1340. The shift keys are unique in many respects. They don't return
  1341. codes that can be detected with INKEY$ or stuffed into the
  1342. keyboard buffer; several can be pressed at the same time; and
  1343. they don't repeat. You can detect 'em with PBWiz, at any rate:
  1344.  
  1345.    IF AltPress% THEN PRINT "An ALT key is pressed."
  1346.    IF CtrlPress% THEN PRINT "A CTRL key is pressed."
  1347.    IF ShiftPress% THEN PRINT "A SHIFT key is pressed."
  1348.  
  1349.    IF LAltPress% THEN PRINT "The LEFT ALT key is pressed."
  1350.    IF LCtrlPress% THEN PRINT "The LEFT CTRL key is pressed."
  1351.    IF LShiftPress% THEN PRINT "The LEFT SHIFT key is pressed."
  1352.  
  1353.    IF RAltPress% THEN PRINT "The RIGHT ALT key is pressed."
  1354.    IF RCtrlPress% THEN PRINT "The RIGHT CTRL key is pressed."
  1355.    IF RShiftPress% THEN PRINT "The RIGHT SHIFT key is pressed."
  1356.  
  1357. NOTE that LAltPress%, LCtrlPress%, RAltPress%, and RCtrlPress%
  1358. are ONLY available for enhanced keyboards. They will not return
  1359. useful results on older keyboards.
  1360.  
  1361.                          Memory (EMS)                    page 30
  1362.  
  1363.  
  1364.  
  1365. This unit provides support for expanded memory. It will work
  1366. with older EMS and EEMS drivers as well as the current EMS 4.0
  1367. standard, with the exception of one or two routines (as noted)
  1368. which take advantage of new capabilities.
  1369.  
  1370. Expanded memory may be present on any type of computer, from
  1371. 8088 PCs to 80486 ATs. It usually comes as a hardware board with
  1372. a software driver for older machines; on ATs, it may be
  1373. implemented using only a software driver which converts it from
  1374. extended memory. Drivers have also been written which make a
  1375. hard disk function as EMS memory. This broad range of use makes
  1376. EMS support invaluable to programs which need extra memory. EMS
  1377. can theoretically support up to 1 gigabyte of RAM, although the
  1378. documented limit as of v4.0 was only 32 megabytes.
  1379.  
  1380. Of course, the first thing you need to know is when dealing with
  1381. EMS is whether any EMS memory actually exists:
  1382.  
  1383.    IF EMSexists% THEN PRINT "EMS exists"
  1384.  
  1385. The EMS version may also be retrieved:
  1386.  
  1387.    EMSver MajorV%, MinorV%
  1388.  
  1389. It would be a good idea to check the EMS version if you plan to
  1390. use any features which are only available as of EMS 4.0, such as
  1391. reallocation.
  1392.  
  1393. Besides a mere existence and version checks, you will want to
  1394. know how much EMS is available:
  1395.  
  1396.    PRINT "Total EMS installed: "; EMStotal%
  1397.    PRINT "Free EMS memory    : "; EMSfree%
  1398.  
  1399. If you actually tried the above two lines, you would get a pair
  1400. of values which don't seem to mean much. The trick is to
  1401. multiply them by 16,384 to convert them to bytes. This is
  1402. because EMS memory is always accessed in pages of 16k bytes
  1403. each. Any time you are dealing with a quantity of EMS memory,
  1404. the quantity will be specified as a number of pages.
  1405.  
  1406.                          Memory (EMS)                    page 31
  1407.  
  1408.  
  1409.  
  1410. Before we get into the mechanics of accessing EMS memory, I'd
  1411. like to bring up an optional routine which can improve access
  1412. speed. It should not be used if your program accesses EMS using
  1413. routines other than the ones included here in PBWiz. If you only
  1414. use these EMS routines, though, you will find that it makes some
  1415. kinds of memory accesses faster. Use 0 for normal (slow) mode.
  1416. Do not use optimization if you are using more than one EMS
  1417. handle!
  1418.  
  1419.    EMSopt Fast%
  1420.  
  1421. Ok, let's get down to the nitty gritty. (Where did that
  1422. expression come from, anyway?!) When you allocate EMS memory,
  1423. you specify the number of pages you want, which must be at least
  1424. 1. If the allocation is successful, you are returned a "handle"
  1425. which you can use to access the allocated memory. Otherwise, you
  1426. get back an error code.
  1427.  
  1428.    EMSopen Pages%, Handle%, ErrCode%
  1429.  
  1430. There are a limited number of handles available under some EMS
  1431. drivers. The EMS spec, as of v4.0, allowed for a maximum of 255
  1432. handles, and it's not unusual for a driver to support only 20 or
  1433. so. Bearing in mind that some of these handles may be used up by
  1434. other applications, such as RAMdisks and caches, this really
  1435. doesn't allow much leeway. Try to use as few handles as
  1436. possible! You may well need to store multiple values in
  1437. different areas of the memory allocated for a single handle,
  1438. rather than allocating a new area of memory for each value.
  1439.  
  1440. Suppose you find you need more memory than you first allocated?
  1441. Or maybe less memory? Well, provided EMS 4.0 or later is in use,
  1442. you can reallocate the block:
  1443.  
  1444.    EMSresize Handle%, Pages%, ErrCode%
  1445.  
  1446. There is no way to reallocate memory under older versions of
  1447. EMS. About the best you could do in that case would be to
  1448. allocate a new area of the desired size, copy over the relevant
  1449. data from the old area, and then deallocate the original area.
  1450. Of course, this assumes that there is enough memory available to
  1451. hold both areas, at least temporarily.
  1452.  
  1453. When you are finished using EMS, you must be sure to return the
  1454. memory you allocated to the system. It is IMPORTANT to do this
  1455. before ending your program. Otherwise, the memory you allocated
  1456. will be "lost" until the next time you boot the computer. Return
  1457. the memory for each handle as follows:
  1458.  
  1459.    EMSclose Handle%
  1460.  
  1461.                          Memory (EMS)                    page 32
  1462.  
  1463.  
  1464.  
  1465. Hmmmm... we've covered EMS detection, status info, allocating
  1466. memory, freeing memory, resizing memory... what's missing here?
  1467. Ah! We haven't discussed how to actually access the memory!
  1468.  
  1469. Accessing EMS memory is quite simple, but it involves a couple
  1470. of steps. EMS is mapped into a 64k block in the low area of
  1471. memory (the area under 1M, which can be directly accessed).
  1472. Since a page is 16k, the EMS block can hold up to four pages at
  1473. a time. To transfer data between normal memory and EMS memory,
  1474. you must map the appropriate page(s) of EMS into the EMS block.
  1475. This is done like so:
  1476.  
  1477.     EMSmap Handle%, PPage%, VPage%
  1478.  
  1479. The PPage% is the physical page, that is, the page number within
  1480. the EMS block (0-3). The VPage% is the virtual page, which is
  1481. the number of a page within the EMS memory associated with
  1482. Handle%.
  1483.  
  1484. Once you've mapped the desired virtual page into a physical
  1485. page, it can be accessed using standard PowerBASIC memory
  1486. commands, such PEEK$ and POKE$. First, set the segment:
  1487.  
  1488.    DEF SEG=EMSseg&
  1489.  
  1490. The offset within a page may be 0-16,383. To get the appropriate
  1491. offset within the EMS block, you must add the offset of the page
  1492. itself. This may be calculated as follows:
  1493.  
  1494.    DataOffset& = OffsetWithinPage% + PageNumber% * 16384&
  1495.  
  1496. One final note: for best compatibility, it would be good to
  1497. avoid using physical page 3 (the last 16k of the EMS block).
  1498. Some EMS drivers don't handle this page with complete accuracy,
  1499. for technical reasons I'm not going to get into right now.
  1500.  
  1501.                          Memory (XMS)                    page 33
  1502.  
  1503.  
  1504.  
  1505. This unit provides support for XMS extended memory. It won't
  1506. work with extended memory unless an XMS driver is present.
  1507.  
  1508. Extended memory is only present in AT-class computers. It is not
  1509. available on older PCs. An XMS driver must also be used. XMS
  1510. drivers are included with MS-DOS 5.0 and Windows 3.0, among
  1511. other things, so this is not a major limitation. XMS can address
  1512. a maximum of 64 megabytes.
  1513.  
  1514. The first thing to check is whether any XMS memory exists:
  1515.  
  1516.    IF XMSexists% THEN PRINT "XMS exists"
  1517.  
  1518. The XMS version may also be retrieved:
  1519.  
  1520.    XMSver MajorV%, MinorV%
  1521.  
  1522. The amount of XMS memory available may be reported in either of
  1523. two different ways: total amount and largest available block.
  1524.  
  1525.    PRINT "Total XMS free    : "; XMStfree&
  1526.    PRINT "Largest free block: "; XMSlfree&
  1527.  
  1528. XMS memory is manipulated in terms of 1,024 byte blocks, so the
  1529. amount of free memory is reported in kilobytes. Any time you are
  1530. dealing with a quantity of EMS memory, the quantity will be
  1531. specified as a number of 1K blocks, except as otherwise noted.
  1532.  
  1533.                          Memory (XMS)                    page 34
  1534.  
  1535.  
  1536.  
  1537. When you allocate XMS memory, you specify the number of
  1538. kilobytes that you want. This may be 0-65535, in theory. Dunno,
  1539. I don't have 64M RAM <grin>. If the allocation is successful,
  1540. you are returned a "handle" which you can use to access the
  1541. allocated memory. Otherwise, you get back an error code.
  1542.  
  1543.    XMSopen KBytes&, Handle%, ErrCode%
  1544.  
  1545. There are a limited number of handles available, although the
  1546. number can be controlled somewhat by a driver parameter. It's
  1547. probably best to use as few handles as possible, to avoid
  1548. running out. You may well want to store multiple values in
  1549. different areas of the memory allocated for a single handle,
  1550. rather than allocating a new area of memory for each value.
  1551.  
  1552. Suppose you find you need more memory than you first allocated?
  1553. Or maybe less memory? Just reallocate the block:
  1554.  
  1555.    XMSresize Handle%, KBytes&, ErrCode%
  1556.  
  1557. When you are finished using XMS, you must be sure to return the
  1558. memory you allocated to the system. It is IMPORTANT to do this
  1559. before ending your program. Otherwise, the memory you allocated
  1560. will be "lost" until the next time you boot the computer. Return
  1561. the memory for each handle as follows:
  1562.  
  1563.    XMSclose Handle%
  1564.  
  1565. To transfer data between normal memory and XMS memory, you must
  1566. provide the segment and offset of the normal memory area (use
  1567. the PowerBASIC functions VARSEG and VARPTR to find the address
  1568. of a variable). The position within XMS memory is specified as a
  1569. long-integer offset starting at zero.
  1570.  
  1571.     XMSread Handle%, Posn&, Bytes&, DSeg%, DOfs%
  1572.  
  1573.     XMSwrite Handle%, Posn&, Bytes&, DSeg%, DOfs%
  1574.  
  1575. Note that the Bytes& to transfer must be an EVEN NUMBER. It is
  1576. not restricted to 64k, however, so you can transfer a great deal
  1577. of data with these routines.
  1578.  
  1579. The XMS spec guarantees a "reasonable" number of interrupt
  1580. windows during a transfer; however, it is possible that you
  1581. might experience some communications dropouts if you do large
  1582. transfers during high-speed telecommunications. If you are
  1583. making heavy demands on the interrupt system, be sure to test
  1584. your program carefully under maximum load conditions.
  1585.  
  1586. A brief demo program is included which shows how to use XMS for
  1587. holding arrays. The demo, XMSDEMO.BAS, shows a two-dimensional
  1588. long integer array. This can be readily altered to support any
  1589. data type composed of an even number of bytes.
  1590.  
  1591.                          Mouse Support                   page 35
  1592.  
  1593.  
  1594.  
  1595. The mouse unit provides full-featured mouse support. You can see
  1596. if a mouse is available and how many buttons it has, get the
  1597. cursor position (either the current position or the position at
  1598. the last press or release of a specified button), set the cursor
  1599. position, change the cursor, set the mouse range, get hardware
  1600. information about the mouse, and so on.
  1601.  
  1602. There are two unusual mouse modes to be aware of. One is text
  1603. mode, which is mapped to a 640x200 virtual display. So, to
  1604. convert the results to text format, you need to divide the
  1605. cursor position by eight and add one. To convert from text
  1606. format, subtract one and multiply by eight.
  1607.  
  1608. The second unusual mode is 320x200 CGA mode, which is also
  1609. mapped to 640x200. To convert the coordinates to this mode,
  1610. divide X by two. To convert from this mode, multiply the X
  1611. coordinate by two.
  1612.  
  1613. All other modes use the actual display coordinates instead of a
  1614. bizarro virtual screen. Why the peculiar CGA and text modes?
  1615. Well, evidently Microsoft never thought there'd be any video
  1616. adapters besides MDA and CGA, and decided to create a single
  1617. virtual screen size that worked for all modes. Not a bad idea, I
  1618. guess, but rather shortsighted. Oh well.
  1619.  
  1620. One other nuisance that you may run into is that the mouse
  1621. cursor can't be directly turned on or off. A "cursor visibility"
  1622. count is maintained-- if the mouse cursor was turned on twice,
  1623. you'll need to turn it off twice before it will actually
  1624. disappear.
  1625.  
  1626. Before using the mouse, you must initialize it. The
  1627. initialization routine also checks to make sure that a mouse is
  1628. installed and tells you how many buttons it has. It's best to
  1629. initialize the mouse after setting the screen mode, so the mouse
  1630. driver understands what mode you're using. Not all mouse drivers
  1631. support all screen modes, but you can reasonably expect any
  1632. current mouse driver to support MDA, CGA, EGA, and VGA. Hercules
  1633. graphics mode is rarely supported, as it must be set through
  1634. direct hardware access rather than the standard techniques, so
  1635. the mouse driver has little way of knowing that you've changed
  1636. the mode.
  1637.  
  1638. The mouse routines will work equally well with two-button or
  1639. three-button rodents. The middle button functions will return 0
  1640. with two-button mice.
  1641.  
  1642.                          Mouse Support                   page 36
  1643.  
  1644.  
  1645.  
  1646. I won't go into great detail on these routines, because they're
  1647. pretty much self-explanatory. The mouse is a fairly easy device
  1648. to deal with.
  1649.  
  1650. You can initialize the mouse driver like so:
  1651.  
  1652.    Buttons% = MouseInit%
  1653.  
  1654. This returns the number of mouse buttons available. If there is
  1655. no mouse, zero will be returned. Initialize the mouse after
  1656. setting the screen mode.
  1657.  
  1658. You can make the mouse cursor visible or invisible. It will
  1659. function just as well in either state. See the previous page for
  1660. some quirks.
  1661.  
  1662.    MouseShow       ' show the cursor
  1663.    MouseHide       ' hide the cursor
  1664.  
  1665. There are many ways to get the mouse cursor position. You can
  1666. get the current position, the position at which the mouse was
  1667. located when a particular button was pressed, or the position
  1668. when a button was released. If you choose a past position, you
  1669. can also find out how many presses or releases of the button
  1670. have taken place since you last checked.
  1671.  
  1672.    X% = MouseWhereX%              ' current X coordinate
  1673.    Y% = MouseWhereY%              ' current Y coordinate
  1674.  
  1675.    MouseLClick Count%, X%, Y%     ' left presses & posn
  1676.    MouseMClick Count%, X%, Y%     ' middle presses & posn
  1677.    MouseRClick Count%, X%, Y%     ' right presses & posn
  1678.  
  1679.    MouseLRelease Count%, X%, Y%   ' left releases & posn
  1680.    MouseMRelease Count%, X%, Y%   ' middle releases & posn
  1681.    MouseRRelease Count%, X%, Y%   ' right releases & posn
  1682.  
  1683. If you'd prefer to find out which buttons are currently pressed,
  1684. no problem:
  1685.  
  1686.    Pressed% = MouseLButton%  ' whether left button is pressed
  1687.    Pressed% = MouseMButton%  ' whether middle button is pressed
  1688.    Pressed% = MouseRButton%  ' whether right button is pressed
  1689.  
  1690. Of course, you can also set the cursor location:
  1691.  
  1692.    MouseLocate X%, Y%        ' set the mouse cursor position
  1693.  
  1694. The mouse cursor range can be restricted to a given area of the
  1695. screen. This area is expressed by giving the upper left corner
  1696. and lower right corner of the rectangular area to which to
  1697. restrict the cursor.
  1698.  
  1699.    MouseWindow X1%, Y1%, X2%, Y2%      ' set mouse cursor range
  1700.  
  1701.                          Mouse Support                   page 37
  1702.  
  1703.  
  1704.  
  1705. There are a variety of cursor shapes available for graphics
  1706. mode:
  1707.  
  1708.     0    hourglass ("please wait, program is working" symbol)
  1709.     1    pointing arrow (default)
  1710.     2    pointing hand
  1711.     3    crosshair
  1712.     4    target (box in a box)
  1713.     5    grabbing hand
  1714.  
  1715. If you have ideas for more, let me know and I'll see what I can
  1716. do. Cursor shapes are not unduly difficult to define.
  1717.  
  1718. The cursor image is set like so:
  1719.  
  1720.    MouseCursorG CursorNr%
  1721.  
  1722.                           SoundBlaster                   page 38
  1723.  
  1724.  
  1725.  
  1726. The SoundBlaster is one of the most popular sound boards around
  1727. these days, despite an early hardware design which conflicts
  1728. with standard parallel ports and a nearly complete absence of
  1729. programming tools. Go figure.
  1730.  
  1731. Anyway, in this here unit, we provide a range of support for the
  1732. SoundBlaster SBSIM driver, and any other sound boards which
  1733. provide drivers with the same functionality. You must load the
  1734. SBSIM driver to use these routines-- just scoot over to your
  1735. SoundBlaster directory and run SBSIM. It will probably give you
  1736. a couple of error messages, which can be safely ignored. They
  1737. refer to optional SoundBlaster capabilities which are probably
  1738. not on your machine. You can test for successful installation
  1739. with the routines in this unit.
  1740.  
  1741. The first thing you must do is check whether the SBSIM driver is
  1742. installed. This is done by checking the software interrupt used
  1743. by the driver. If the number returned is zero, SBSIM is not
  1744. installed. You probably won't need to know the actual interrupt
  1745. number itself, but it's returned in case you want to use SBSIM
  1746. in ways that aren't supported by PBWiz.
  1747.  
  1748.    IntNr% = SBInt%
  1749.  
  1750. SBSIM is actually a super-driver. It loads a number of other
  1751. drivers, depending on the configuration of the sound board and
  1752. the SBSIM.CFG file. Possible sub-drivers include an FM driver
  1753. (.CMF files), disk and memory voice drivers (.VOC files), a MIDI
  1754. driver (.MID files), and an auxiliary driver. You can check
  1755. which drivers are available like so:
  1756.  
  1757.    SBGetDrivers FM%, DskVoice%, MemVoice%, Auxiliary%, MIDI%
  1758.  
  1759. The difference between the disk and memory voice drivers is that
  1760. the disk voice driver can play a .VOC file directly, whereas the
  1761. memory voice driver plays .VOC files that have been loaded into
  1762. XMS memory. The auxiliary driver is not supported by the current
  1763. version of PBWiz. The routine returns 0 if the driver is not
  1764. installed, or -1 if it is.
  1765.  
  1766. A similar routine can be used to detect which drivers are in use
  1767. at the moment:
  1768.  
  1769.    SBGetActive FM%, DskVoice%, MemVoice%, Auxiliary%, MIDI%
  1770.  
  1771. The version of the SBSIM driver can be retrieved as well:
  1772.  
  1773.    SBGetVer MajorV%, MinorV%
  1774.  
  1775. Given a version of 1.20, this would return MajorV% = 1 and
  1776. MinorV% = 20. The minor version number is generally not of much
  1777. importance and is mostly useful for informational purposes.
  1778.  
  1779.                           SoundBlaster                   page 39
  1780.  
  1781.  
  1782.  
  1783. The volume level for various SBSIM devices can be retrieved or
  1784. set for both stereo and mono SoundBlasters. Only the "left"
  1785. speaker is active in the case of mono SoundBlasters.
  1786.  
  1787.    SBGetVolume Source%, LeftVol%, RightVol%
  1788.    SBSetVolume Source%, LeftVol%, RightVol%
  1789.  
  1790. Sources may be any of the following:
  1791.  
  1792.    0    master volume         4    line in
  1793.    1    voice                 5    microphone
  1794.    2    FM                    6    PC speaker
  1795.    3    CD player
  1796.  
  1797. There are several ways to set up the MIDI channel mapper. This
  1798. is done like so:
  1799.  
  1800.    SBMapMIDI MapNr%
  1801.  
  1802. You may choose from the following mappers:
  1803.  
  1804.    0    general mapper
  1805.    1    basic mapper
  1806.    2    extended mapper
  1807.  
  1808. That pretty well covers the generic status routines. The rest of
  1809. the SoundBlaster routines are concerned with actually playing
  1810. sounds, and related functions such as pause and resume.
  1811.  
  1812. Before playing a sound, you need to initialize the sound driver.
  1813. You may choose to play a sound file directly from disk (in the
  1814. case of .CMF, .VOC, and .MID files) or from memory (.VOC only).
  1815. Although all sounds are played in the background and do not
  1816. interfere with your program operation, you will get smoother
  1817. response by loading .VOC files into XMS memory beforehand, if
  1818. plenty of memory is available.
  1819.  
  1820. You initialize a file to play directly from disk like so:
  1821.  
  1822.    SBInitSrcFile DriverNr%, FileName$, ErrCode%
  1823.  
  1824. The file name must include the extension. You may choose from
  1825. the following drivers:
  1826.  
  1827.    1    .CMF    FM synthesis
  1828.    2    .VOC    disk voice
  1829.    5    .MID    MIDI
  1830.  
  1831. Remember, this only initializes the file to play. It does not
  1832. actually start playing the sound. We'll get to that shortly.
  1833.  
  1834.                           SoundBlaster                   page 40
  1835.  
  1836.  
  1837.  
  1838. Files can also be loaded into XMS memory before playing, in the
  1839. case of .VOC files only. A handle is returned which can later be
  1840. used to identify the sound to play. Normally, you should set the
  1841. handle to zero before calling the routine, which forces the
  1842. routine to use the first available handle it can find. The real
  1843. handle is returned to you.
  1844.  
  1845.    SBLoadXMS FileName$, Handle%, ErrCode%
  1846.  
  1847. In the case of sounds loaded into memory, the initialization is
  1848. done as follows:
  1849.  
  1850.    SBInitSrcXMS Handle%, ErrCode%
  1851.  
  1852. When you have finished using a handle, be sure to free it, or
  1853. the associated memory will be allocated until the computer is
  1854. rebooted. Freeing a handle is easy:
  1855.  
  1856.    SBFreeXMS Handle%
  1857.  
  1858. You can check for the number of free handles as well:
  1859.  
  1860.    Free% = SBFreeHandles%
  1861.  
  1862. And, for that matter, see if a specific handle is free:
  1863.  
  1864.    Free% = SBIsFree%(Handle%)
  1865.  
  1866. Once the sound driver is initialized, you may start it playing,
  1867. pause it, resume, check its status, or stop it:
  1868.  
  1869.    SBPlay Driver%
  1870.    SBPause Driver%
  1871.    SBResume Driver%
  1872.    SBStop Driver%
  1873.    State% = SBStatus% (Driver%)
  1874.  
  1875. The driver may be any of the following:
  1876.  
  1877.    1   .CMF    FM synthesis
  1878.    2   .VOC    disk voice
  1879.    3   (XMS)   memory voice
  1880.    5   .MID    MIDI
  1881.  
  1882. Note that the status value is often -1 while a driver is playing
  1883. and 0 while it isn't, but different status values may be
  1884. embedded in the sound file. If your aim is to determine whether
  1885. a sound is still playing, use SBGetActive instead of SBStatus%.
  1886.  
  1887. See the PLAYVOC.BAS file for a demo of how these routines work
  1888. together in practice.
  1889.  
  1890.                             Strings                      page 41
  1891.  
  1892.  
  1893.  
  1894. One of the true strengths of BASIC in general, and PowerBasic in
  1895. particular, is its powerful string handling capability. I'd be
  1896. remiss if I didn't provide extensions which improve it even
  1897. further.
  1898.  
  1899. The simplest of the PBWiz string routines may seem somewhat
  1900. whimsical, but it has proven useful to me on several occasions
  1901. in the past. It reverses the order of characters in a string.
  1902.  
  1903.    Reverse St$
  1904.  
  1905. One of the places this has come in useful is in searching a
  1906. string from the end-- a reverse INSTR routine:
  1907.  
  1908.    RInstr MainSt$, SubSt$, Posn%
  1909.  
  1910. Rather than returning the first occurrence of a substring within
  1911. a main string, it returns the last occurrence. Another handy
  1912. string search allows you to search for various types of
  1913. characters, rather than a specific substring:
  1914.  
  1915.    TInstr MainSt$, Types%, Posn%
  1916.  
  1917. The type(s) may be specified using any combination of the
  1918. following. Just add them together.
  1919.  
  1920.     1    alphabetic
  1921.     2    numeric
  1922.     4    symbolic
  1923.     8    control
  1924.    16    graphics
  1925.    32    space
  1926.  
  1927. Since you can search for any specific types, you can also
  1928. readily invert the search to look for any characters that are
  1929. NOT of a given type or types:
  1930.  
  1931.    Types% = NOT Types%
  1932.  
  1933. This gives you complete control. A typical use for TInstr might
  1934. be to clean up user input and make sure that it's valid. It's
  1935. also good for parsing.
  1936.  
  1937.                             Strings                      page 42
  1938.  
  1939.  
  1940.  
  1941. Another routine that is useful for cleaning up and parsing user
  1942. input is called Crunch. It allows you to eliminate adjacent
  1943. duplicates of a character or list of characters. One use for
  1944. this, for instance, would be to eliminate repeated spaces,
  1945. converting an input string from "*.* *.BAK /B" to a more
  1946. manageable "*.* *.BAK /B".
  1947.  
  1948.    Result$ = Crunch$(St$, CharList$)
  1949.  
  1950. There are a pair of routines that you'll find valuable if you
  1951. need to check the validity of a string. These are designed to be
  1952. compatible with the Xmodem and Ymodem file transfer protocols,
  1953. so you can use them for error checking in telecommunications as
  1954. well.
  1955.  
  1956.    Chk% = CheckSum% (St$)
  1957.  
  1958.    CRC16 St$, HiCRC%, LoCRC%
  1959.  
  1960. Another pair of string routines provide a simple encryption and
  1961. decryption system for text. The method used is not particularly
  1962. secure but are very fast and will be adequate for many purposes.
  1963. As always, it helps to use a long and/or complex password.
  1964.  
  1965.    Cipher St$, Password$          ' cipher (unprintable)
  1966.    CipherP St$, Password$         ' cipher (printable)
  1967.  
  1968. Both of these routines will encipher text on the first
  1969. run-through and decipher on the second, so you can use the same
  1970. routine either to encrypt or decrypt a message. They are
  1971. different in one respect: the encrypted result of Cipher may
  1972. contain control characters, so it can't be used in a plain
  1973. sequential-access file. The CipherP routine does not allow use
  1974. of extended ASCII characters (CHR$(128) - CHR$(255)), as it sets
  1975. the high bit on each character after encrypting it. This causes
  1976. the results of CipherP to be printable (and useful in
  1977. sequential-access files), although they will look very strange.
  1978.  
  1979. The strings are encrypted (or decrypted) in place. This provides
  1980. a certain extra measure of security for encryption-- the
  1981. original plaintext strings are not left floating around in
  1982. memory where someone might see them.
  1983.  
  1984. One function is as much a file manipulation routine as it is a
  1985. string function. It allows you to compare a file name to a file
  1986. pattern (which may contain wildcards) to see if they match. Only
  1987. bare filespecs are supported-- you may not use drive or path
  1988. specifications in the names.
  1989.  
  1990.    IF MatchFile% (Pattern$, Filename$) THEN PRINT Filename$
  1991.  
  1992.                             Strings                      page 43
  1993.  
  1994.  
  1995.  
  1996. The MatchFile function can be used in creating your own
  1997. DOS-style utilities: DIR, COPY, and so forth. Besides the usual
  1998. "accept file if it matches" approach, it can also be used to
  1999. implement the opposite: "exclude file if it matches." This gives
  2000. you more flexibility than DOS itself supplies.
  2001.  
  2002. The PowerBASIC compiler provides a very nice function called
  2003. Extract$. This allows you to retrieve a substring running from
  2004. the left side of a main string to a specified character
  2005. delimiter. Not bad, but it might be handy to be able to grab a
  2006. numbered substring from any part of a main string, and to be
  2007. able to use a substring delimiter. For instance, you might load
  2008. a record from a database which contains an address, where each
  2009. line is delimited by a carriage return and linefeed. Rather than
  2010. mucking around with Extract$, which really wasn't designed with
  2011. that sort of thing in mind, you'd do better to use the PBWiz
  2012. function called DelimExtract$:
  2013.  
  2014.    SubSt$ = DelimExtract$(St$, Delimiter$, Index%)
  2015.  
  2016. The index starts at 1 with the first substring. If you choose
  2017. the index of a substring which doesn't actually exist, a null
  2018. string will be returned.
  2019.                      Telecommunications                 page 44
  2020.  
  2021.  
  2022.  
  2023. This unit provides an assortment of low-level, buffered,
  2024. interrupt-driven telecommunications services. It allows
  2025. comprehensive control over communications, includes COM3 and
  2026. COM4, and doesn't require error trapping. It won't fiddle with
  2027. the DTR unless you tell it to do so. The major limitation is
  2028. that you may use only a single comm port at a time.
  2029.  
  2030. Before you can use communications, you must initialize the
  2031. communications handler. If you didn't have PBWiz, you would
  2032. probably use something like:
  2033.  
  2034.    OPEN "COM1:2400,N,8,1,RS,CS,DS" AS #1
  2035.  
  2036. With PBWiz, you do not have to set the speed, parity, and so
  2037. forth. Communications will proceed with whatever the current
  2038. settings are, unless you choose to specify your own settings.
  2039. When you initialize the comm handler, you specify only the port
  2040. number (1-4):
  2041.  
  2042.    TCInit CPort%, ErrCode%
  2043.  
  2044. When you are done with the telecomm routines, you must terminate
  2045. them. In BASIC, this would look something like:
  2046.  
  2047.    CLOSE #1
  2048.  
  2049. With the PBWiz routines, though, you would use this instead:
  2050.  
  2051.    TCDone
  2052.  
  2053. The PBWiz "TCDone" does not drop the DTR, unlike BASIC's
  2054. "CLOSE". This means that the modem will not automatically be
  2055. told to hang up. With PBWiz, you have complete control over the
  2056. DTR with the TCDTR routine. Use a value of zero to drop the DTR
  2057. or nonzero to raise the DTR:
  2058.  
  2059.    TCDTR DTRstate%
  2060.  
  2061. You may set the speed of the comm port to any baud rate from
  2062. 1-65,535. If you will be dealing with comm programs that were
  2063. not written using PBWiz, you may wish to restrict that to the
  2064. more common rates: 300, 1200, 2400, 4800, 9600, 19200, 38400,
  2065. and 57600.
  2066.  
  2067.    TCSpeed Baud&
  2068.  
  2069.                      Telecommunications                 page 45
  2070.  
  2071.  
  2072.  
  2073. The parity, word length, and stop bits can also be specified.
  2074. You may use 1-2 stop bits, 6-8 bit words, and parity settings of
  2075. None, Even, Odd, Mark, or Space. Nearly all BBSes use settings
  2076. of None, 8 bit words, and 1 stop bit, although you will
  2077. sometimes see Even, 7 bit words, and 1 stop bit. The other
  2078. capabilities are provided for dealing with mainframes and other
  2079. systems which may require unusual communications parameters.
  2080.  
  2081. When specifying parity, only the first character in the string
  2082. is used, and uppercase/lowercase distinctions are ignored. Thus,
  2083. using either "none" or "N" would specify that no parity is to be
  2084. used.
  2085.  
  2086.    TCParms Parity$, WordLength%, StopBits%
  2087.  
  2088. If your program needs to be aware of when a carrier is present,
  2089. it can check the carrier detect signal from the modem with the
  2090. TCCarrier function. This function returns zero if no carrier is
  2091. present:
  2092.  
  2093.    IF TCCarrier% THEN
  2094.       PRINT "Carrier detected"
  2095.    ELSE
  2096.       PRINT "No carrier"
  2097.    END IF
  2098.  
  2099. Suppose, though, that you need to know immediately when someone
  2100. has dropped the carrier? It wouldn't be too convenient to have
  2101. to spot TCCarrier functions all over your program! In that case,
  2102. try the "ON TIMER" facility provided by BASIC for keeping an eye
  2103. on things. It will enable you to check the carrier at specified
  2104. intervals and act accordingly. Here's a brief framework for
  2105. writing such code:
  2106.  
  2107.    ON TIMER(30) GOSUB CarrierCheck
  2108.    TIMER ON
  2109.    ' ...your program goes here...
  2110. CarrierCheck:
  2111.    IF TCCarrier% THEN    ' if the carrier is present...
  2112.       RETURN             ' ...simply resume where we left off
  2113.    ELSE                  ' otherwise...
  2114.       RETURN Restart     ' ...return to the "Restart" label
  2115.    END IF
  2116.  
  2117. To get a character from the comm port, use the TCInkey$
  2118. function. This is available in byte and string flavors:
  2119.  
  2120.    ch$ = TCInkey$
  2121.    ch% = TCInkey0%
  2122.  
  2123.                      Telecommunications                 page 46
  2124.  
  2125.  
  2126.  
  2127. To send a string to the comm port, use TCWrite:
  2128.  
  2129.    TCWrite St$
  2130.  
  2131. If you are dealing strictly with text, you may want to have a
  2132. carriage return and a linefeed added to the end of the string:
  2133.  
  2134.    TCWriteLn St$
  2135.  
  2136. If you'd like to know how many bytes are waiting in the input
  2137. buffer or output buffer, there are functions which will tell
  2138. you:
  2139.  
  2140.    PRINT "Bytes in input buffer:"; TCInStat%
  2141.    PRINT "Bytes in output buffer:"; TCOutStat%
  2142.  
  2143. If you would like to clear the I/O buffers for some reason, you
  2144. can do that too. The following routines clear the buffers,
  2145. discarding anything which was waiting in them:
  2146.  
  2147.    TCFlushIn
  2148.    TCFlushOut
  2149.  
  2150. If you're using a fast modem (9600 bps or greater), you should
  2151. turn on hardware flow control for reliable communications:
  2152.  
  2153.    TCFlowCtl (-1)
  2154.  
  2155. To get some idea of how these routines all tie together in
  2156. practice, see the MINITERM.BAS example program. It provides a
  2157. tiny "dumb terminal" program to demonstrate the PBWiz comm
  2158. handler. The modem to use is selected via command-line switch:
  2159.  
  2160.    /COM1   use COM1
  2161.    /COM2   use COM2
  2162.    /COM3   use COM3
  2163.    /COM4   use COM4
  2164.  
  2165. By default, the MINITERM.BAS program will use COM1 at 2400 baud
  2166. with no parity, 8 bit words and 1 stop bit. You can exit the
  2167. program by pressing Alt-X.
  2168.  
  2169.                      Telecommunications                 page 47
  2170.  
  2171.  
  2172.  
  2173.  
  2174. A few notes on the ins and outs of telecommunications...
  2175.  
  2176. The DTR signal is frequently used to control the modem. When the
  2177. DTR is "raised" or "high", the modem knows that we're ready to
  2178. do something. When the DTR is "dropped" or "low", the modem
  2179. knows that we're not going to do anything. In most cases, this
  2180. tells it to hang up or disconnect the phone line. Some modems
  2181. may be set to ignore the DTR, in which case it will not
  2182. disconnect when the DTR is dropped. Usually this can be fixed by
  2183. changing a switch on the modem. On some modems, a short software
  2184. command may suffice.
  2185.  
  2186. The DTR is generally the best way to disconnect. The Hayes "ATH"
  2187. command is supposed to hang up, but it doesn't work very well.
  2188.  
  2189. The PBWiz comm handler makes sure the DTR is raised when TCInit
  2190. is used. It does not automatically drop the DTR when TCDone is
  2191. used, so you can keep the line connected in case another program
  2192. wants to use it. If this is not suitable, just use TCDTR to drop
  2193. the DTR. Your program must always use TCDone before it exits,
  2194. but it need only drop the DTR if you want it that way.
  2195.  
  2196. If you want to execute another program via SHELL, it is ok to
  2197. leave communications running as long as control will return to
  2198. your program when the SHELLed program is done. In that case, the
  2199. input buffer will continue to receive characters from the comm
  2200. port unless the SHELLed program provides its own comm support.
  2201. The output buffer will likewise continue to transmit characters
  2202. unless overruled.
  2203.  
  2204.                         Text-mode Video                  page 48
  2205.  
  2206.  
  2207.  
  2208. The graphical interface has become a "sexy" thing to have these
  2209. days, but there are still many good reasons to work in text
  2210. mode. It's relatively fast, it works on all monitors, and it's
  2211. often the most appropriate choice. Graphics is overkill for many
  2212. applications. Besides, you can't redirect graphics to a file or
  2213. to the printer.
  2214.  
  2215. Come to think of it, text displayed by the PRINT statement can't
  2216. be redirected either. Fortunately, we can fix that. All it takes
  2217. is sending the output through DOS:
  2218.  
  2219.    DOSPrint St$         ' print to the current output device
  2220.  
  2221. A nice thing about DOS output is that ANSI display codes will
  2222. work if you have an ANSI driver (such as ANSI.SYS) installed. If
  2223. you're working with existing text, such as captured output from
  2224. a BBS or from an ANSI art program like TheDraw, you can just use
  2225. the DOSPrint routine to handle it. If you're looking to do your
  2226. own ANSI output, though, there's an easier way than looking up
  2227. the individual codes and sending 'em out one at a time. The
  2228. following routines send the appropriate ANSI codes to the
  2229. current DOS output device:
  2230.  
  2231.    DOSCls                    ' clear the screen
  2232.    DOSColor Fore%, Back%     ' set the screen colors
  2233.    DOSLocate Row%, Column%   ' set the cursor position
  2234.  
  2235. The primary advantage of DOS output is that it can be
  2236. intercepted. It normally goes to the screen, but you can
  2237. redirect it to a file, printer, or comm port, among other
  2238. things. You can also be sure that DOS output will work
  2239. reasonably in a multitasking environment like DESQview or
  2240. Windows, instead of messing up the screen. The disadvantage is
  2241. that DOS output is fairly slow. It's great for command line
  2242. utilities, but not if you plan to do any fancy screen work. For
  2243. that, you probably want direct-access techniques.
  2244.  
  2245. The direct screen access provided by PBWiz is rude, crude... and
  2246. faster than a greased euphemism. It can't be redirected, doesn't
  2247. handle control codes, and won't even update the cursor position.
  2248. In return for this lack of amenities, it gives you two very
  2249. valuable things: complete control and raw speed.
  2250.  
  2251.                         Text-mode Video                  page 49
  2252.  
  2253.  
  2254.  
  2255. The direct-access replacement for PRINT works like so:
  2256.  
  2257.    XQPrint St$, Row%, Column%, Attr%
  2258.  
  2259. Now, you might guess that St$ is the text to print, and Row% and
  2260. Column% are where to print it. The Attr% may seem a bit more
  2261. opaque. The Attr% is the color to use-- actually, both the
  2262. foreground and background colors. These are combined into a
  2263. single value because that's the way the display controller wants
  2264. to see it. Remember, the emphasis here is on speed, not
  2265. necessarily convenience! So how do you calculate an attribute
  2266. given the foreground and background colors?
  2267.  
  2268.    Attr% = CalcAttr% (Fore%, Back%)
  2269.  
  2270. An Attr% value is never less than 0 or greater than 255-- it
  2271. will fit into a single byte. That may be useful to know for
  2272. storage purposes. In any event, keep the Attr% in mind, because
  2273. we'll be seeing more of it in the future. By the way, you can
  2274. unpack an attribute into foreground and background colors too,
  2275. if need be:
  2276.  
  2277.    UnCalcAttr Attr%, Fore%, Back%
  2278.  
  2279. In addition to XQPrint, there is a routine which allows you to
  2280. overlay existing text rather than replacing it. It skips blank
  2281. spaces rather than putting them on the display.
  2282.  
  2283.    XQPrintOver St$, Row%, Column%, Attr%
  2284.  
  2285. The direct-access routines allow for one, and only one, option
  2286. which might slow them down. The IBM CGA and some clone CGAs
  2287. flicker horribly if you access their display memory directly.
  2288. There is a software fix, but it slows the display down
  2289. tremendously. Still, if your program is run on such a CGA,
  2290. you'll want to stop the flickering. This may be done so:
  2291.  
  2292.    AntiSnow Slow%       ' any nonzero value to stop flicker
  2293.  
  2294. You should provide a command-line switch or configuration option
  2295. to force anti-flicker support. Don't do it by default, as the
  2296. slowdown is very noticeable.
  2297.  
  2298. The "menu" approach has become the standard way of allowing a
  2299. user to choose between various program options. There are many
  2300. ways of designing a menu, but they almost always require a
  2301. "highlight" to show the current choice. This highlight is
  2302. generally handled by changing the color of the chosen item:
  2303.  
  2304.    ReColorArea TopRow%, LftCol%, BotRow%, RtCol%, Attr%
  2305.  
  2306.                         Text-mode Video                  page 50
  2307.  
  2308.  
  2309.  
  2310. Pop-up windows have become ubiquitous. Naturally, PBWiz supports
  2311. them too:
  2312.  
  2313.    PopWindow TRow%, LCol%, BRow%, RCol%, Frame%,
  2314.       Attr%, Grow%, Shade%, TFore%, Title$   ' use one line!
  2315.  
  2316. The first four parameters specify the upper left corner and
  2317. lower right corner of the window. The window frame, if any, is
  2318. created just outside these coordinates. If you choose a shadow
  2319. for a 3D effect, that will extend further outside the window.
  2320. Keep this in mind if you want to save the part of the screen
  2321. under the window... but we'll get to that! Let's see what
  2322. options are available for the frame type:
  2323.     0   no frame
  2324.     1   single lines
  2325.     2   double lines
  2326.     3   single horizontal, double vertical lines
  2327.     4   double horizontal, single vertical lines
  2328.     5   block graphic lines
  2329.  
  2330. These are the available shadows:
  2331.    -3   transparent shadow on the right
  2332.    -2   transparent shadow on the left
  2333.    -1   solid black shadow on the left
  2334.     0   no shadow
  2335.    1+   shadow attribute (use CalcAttr) for a colored shadow
  2336.  
  2337. Options for growing windows are as follows:
  2338.    -1   grow as fast as possible
  2339.     0   pop onto the screen
  2340.    1+   grow with a specified delay in milliseconds
  2341.         (15 works well for me)
  2342.  
  2343. The TFore% parameter is the foreground color to use for the
  2344. title (Title$), if any (use "" for no title).
  2345.  
  2346. It is worth noting that the "milliseconds" value is only rather
  2347. approximate. The delay is based on the video card, and is fairly
  2348. similar on any computer system, but there will be a noticeable
  2349. difference between (say) an XT with a CGA and a 486 with a VGA.
  2350. Still, it's a useful delay-- if not really precise, it's at
  2351. least fairly accurate, and it has a fine resolution. You can
  2352. access this delay yourself:
  2353.  
  2354.    DelayV MilliSeconds%
  2355.  
  2356.                         Text-mode Video                  page 51
  2357.  
  2358.  
  2359.  
  2360. I mentioned screen saves a bit earlier. With PBWiz, you can save
  2361. any part of a screen, and restore it later to the same place or
  2362. an entirely different area. This also offers the possibility of
  2363. creating a screen image, storing it in a file, and reading it
  2364. into your program, among other things. It works like this:
  2365.  
  2366.    Scr$ = ScreenSave$ (TopRow%, LftCol%, BotRow%, RtCol%)
  2367.  
  2368.    ScreenRestore Scr$, TopRow%, LftCol%
  2369.  
  2370. It takes about 4K to store a full 80x25 text screen. The exact
  2371. calculation is Bytes = Rows * Columns * 2 + 2, if you care to
  2372. figure it out yourself. You can also take advantage of a PBWiz
  2373. routine to handle it:
  2374.  
  2375.    Bytes% = CalcSize% (TopRow%, LftCol%, BotRow%, RtCol%) + 2
  2376.  
  2377. Finally, we are left with a series of routines which let you
  2378. scroll (or clear) any part of the screen:
  2379.  
  2380.    ScrollDown TRow%, LCol%, BRow%, RCol%, Times%, Attr%
  2381.    ScrollLeft TRow%, LCol%, BRow%, RCol%, Times%, Attr%
  2382.    ScrollRight TRow%, LCol%, BRow%, RCol%, Times%, Attr%
  2383.    ScrollUp TRow%, LCol%, BRow%, RCol%, Times%, Attr%
  2384.  
  2385. If you attempt to scroll zero times, or more times than there
  2386. are rows (or columns, depending on which way you scroll), the
  2387. specified area of the screen will be cleared. The Attr% gives
  2388. the color to use on the cleared area of the screen.
  2389.  
  2390.                             Credits                      page 52
  2391.  
  2392.  
  2393.  
  2394. I'd like to thank Dave Navarro for letting me in on the world of
  2395. PowerBASIC. His help has been most valuable to me in many
  2396. respects. Without him, this library would have taken much longer
  2397. to get off the ground or would perhaps not even exist.
  2398.  
  2399. I would also like to thank Spectra, publishers of PowerBASIC,
  2400. for sending me the evaluation copy of PowerBASIC which led to my
  2401. decision to write this library. I have also found their tech
  2402. support to be outstanding and of great assistance.
  2403.  
  2404. My thanks also to the good people who have registered PBWiz.
  2405. You're the ones that make PBWiz happen.
  2406.  
  2407.